dbread.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. '''
  2. Created on Jun 21, 2018 @author: levente.marton
  3. '''
  4. import os
  5. import shutil
  6. import re
  7. import datetime
  8. from dataclasses import dataclass
  9. from pypxlib import Table
  10. @dataclass
  11. class Company:
  12. name: str
  13. vat_code: str
  14. reg_number: str
  15. address: str
  16. location: str
  17. county: str
  18. shortname: str
  19. obs: str
  20. adm: str
  21. admp: str
  22. admcnp: str
  23. mail: str = None
  24. price: int = None
  25. @dataclass
  26. class Account:
  27. clasa: str
  28. simbol: str
  29. denumire: str
  30. soldid: str = None
  31. soldic: str = None
  32. precedentd: str = None
  33. precedentc: str = None
  34. curentd: str = None
  35. curentc: str = None
  36. def soldf(self, type_):
  37. if type_ == 'd':
  38. return round((self.soldid + self.precedentd + self.curentd) -
  39. (self.soldic + self.precedentc + self.curentc))
  40. elif type_ == 'c':
  41. return round((self.soldic + self.precedentc + self.curentc) -
  42. (self.soldid + self.precedentd + self.curentd))
  43. class WinMentor(object):
  44. '''
  45. Class to read winmentor database
  46. '''
  47. def __init__(self, winment_path=os.getenv('WINMENT', 'c:/winment/data/').replace('\\', '/')):
  48. '''
  49. Constructor
  50. '''
  51. self.winment_path = winment_path
  52. @property
  53. def get_winment_path(self):
  54. return self.winment_path
  55. @get_winment_path.setter
  56. def set_winment_path(self, value):
  57. # self.value = value
  58. self.winment_path = value
  59. return self.winment_path
  60. def update_copy(self, db_file) -> str:
  61. '''returns the updated version of a .db file
  62. :param db_file is a .db from winmentor'''
  63. # self._to_file = db_file
  64. _, file_name = os.path.split(db_file)
  65. dir_list = db_file.split('/')
  66. # !!NOTE: if path is ?/winment/data/ then this must be 3 & 5
  67. # if is ?/winment/winment/data/ then 4 & 6
  68. dir_to_file = dir_list[4]
  69. if len(dir_list) != 6:
  70. os.makedirs(os.path.join('cash', dir_to_file), exist_ok=True)
  71. cashed_copy_mtime = -1
  72. if os.path.isfile(os.path.join('./cash/', dir_to_file, file_name)):
  73. cashed_copy_mtime = os.stat(os.path.join('./cash/', dir_to_file, file_name)).st_mtime
  74. if os.stat(db_file).st_mtime != cashed_copy_mtime:
  75. shutil.copy2(db_file, os.path.join('./cash', dir_to_file))
  76. return(os.path.join('./cash/', dir_to_file, file_name))
  77. else:
  78. os.makedirs('cash', exist_ok=True)
  79. cashed_copy_mtime = -1
  80. if os.path.isfile('./cash/' + file_name):
  81. cashed_copy_mtime = os.stat('./cash/' + file_name).st_mtime
  82. if os.stat(db_file).st_mtime != cashed_copy_mtime:
  83. shutil.copy2(db_file, './cash')
  84. return('./cash/' + file_name)
  85. def make_list(self, db_file, headers) -> list:
  86. '''returns a list of lists with elements from a given db file
  87. :param db_file is .db file from winmentor
  88. :param headers are the fields of the specified db file'''
  89. file_name = self.update_copy(db_file)
  90. with Table(file_name) as table:
  91. m_list = []
  92. for row in table:
  93. item = []
  94. for header in headers:
  95. element = row[header]
  96. if type(element) == datetime.date:
  97. if element < datetime.date(1999, 1, 1):
  98. element = ''
  99. item.append(element)
  100. m_list.append(item)
  101. return m_list
  102. def make_sal_list(self, file_1, file_2) -> list:
  103. '''returns list with all_ employees in current month
  104. :param file_1 is npers.db from shortname
  105. :param file_2 is likisal.db shortname/current_month'''
  106. file_name1 = self.update_copy(self.winment_path + file_1)
  107. file_name2 = self.update_copy(self.winment_path + file_2)
  108. with Table(file_name1) as perss, Table(file_name2) as sals:
  109. empl_all = []
  110. # for field in perss.fields:
  111. for sal in sals:
  112. for pers in perss:
  113. # print(pers)
  114. if pers.Cod == sal.Cod:
  115. empl = []
  116. empl.append(pers.Cod)
  117. empl.append(f'{pers.Nume} {pers.Prenume}')
  118. empl.append(pers.DataAngF.strftime('%d-%m-%Y'))
  119. empl.append(sal.VenitBrut)
  120. empl.append(sal.SalRealizat)
  121. empl.append(sal.CO)
  122. empl.append(round(sal.SalOra, 2))
  123. empl.append(sal.ContribAngajat)
  124. empl.append(round(sal.ContribAngajator, 2))
  125. empl.append(sal.VenitNet)
  126. empl.append(sal.Impozit)
  127. empl.append(sal.SalarNet)
  128. empl.append(sal.OreLucrate)
  129. empl.append(sal.ZileLuk)
  130. empl.append(sal.ZileCO)
  131. empl_all.append(empl)
  132. return empl_all
  133. def gen_firms(self, db_file, headers) -> list:
  134. '''generates company list from firme.db file
  135. :param db_file is firme.db from winment/data folder'''
  136. file_db = self.update_copy(self.winment_path + db_file)
  137. with Table(file_db) as table:
  138. for row in table:
  139. yield [row[header] for header in headers]
  140. def firmlist(self, headers, db_file='/firme.db', ban=None) -> list:
  141. '''returns company list from firme.db file
  142. :param db_file is firme.db from winment/data folder
  143. :param list headers is the fields from firme.db file
  144. :param list|str ban companies excluded from list'''
  145. comp_list = []
  146. for a_company in self.gen_firms(db_file, headers):
  147. company = Company(
  148. name=a_company[0],
  149. vat_code=a_company[1],
  150. reg_number=a_company[2],
  151. address=a_company[3],
  152. location=a_company[4],
  153. county=a_company[5],
  154. shortname=a_company[6],
  155. adm=a_company[7],
  156. admp=a_company[8],
  157. admcnp=a_company[9],
  158. obs=a_company[10])
  159. comp_list.append(company)
  160. if ban:
  161. if type(ban) is list:
  162. for r in comp_list:
  163. for company_shortname in ban:
  164. if company_shortname == r.shortname:
  165. comp_list.remove(r)
  166. else:
  167. for r in comp_list:
  168. if r == ban:
  169. comp_list.remove(r)
  170. return comp_list
  171. def filtered_firmlist(self, headers, db_file='/firme.db', ban=None) -> list:
  172. '''generates company list from firme.db file
  173. :param db_file is firme.db from winment/data folder
  174. :param list headers is the fields from firme.db file
  175. :param list|str ban companies excluded from list'''
  176. if ban:
  177. if type(ban) is list:
  178. for a_company in self.gen_firms(db_file, headers):
  179. company = Company(
  180. name=a_company[0],
  181. vat_code=a_company[1],
  182. reg_number=a_company[2],
  183. address=a_company[3],
  184. location=a_company[4],
  185. county=a_company[5],
  186. shortname=a_company[6],
  187. adm=a_company[7],
  188. admp=a_company[8],
  189. admcnp=a_company[9],
  190. obs=a_company[10])
  191. if a_company[6] not in ban:
  192. yield [company.name, company.vat_code, company.reg_number,
  193. company.address, company.location, company.county,
  194. company.shortname, company.adm, company.admp,
  195. company.admcnp, company.obs]
  196. else:
  197. for a_company in self.gen_firms(db_file, headers):
  198. company = Company(
  199. name=a_company[0],
  200. vat_code=a_company[1],
  201. reg_number=a_company[2],
  202. address=a_company[3],
  203. location=a_company[4],
  204. county=a_company[5],
  205. shortname=a_company[6],
  206. adm=a_company[7],
  207. admp=a_company[8],
  208. admcnp=a_company[9],
  209. obs=a_company[10])
  210. yield [company.name, company.vat_code, company.reg_number,
  211. company.address, company.location, company.county,
  212. company.shortname, company.adm, company.admp,
  213. company.admcnp, company.obs]
  214. def get_last_month(self, short_name) -> str:
  215. '''returns last month of a company in format YYYY_MM
  216. :param str short_name is the company shortname'''
  217. short_name = self.winment_path + short_name
  218. month_folders = [f for f in os.listdir(short_name) if re.match(r'[0-9_]+$', f)]
  219. month_folders.reverse()
  220. return month_folders[0]
  221. def get_bank_accounts(self, short_name, db_file='/nbanca.db'):
  222. #.......................................................................
  223. # TO DO: put an all_ parameter to yield all_ accounts or
  224. # just one,
  225. # make a named tuple with bank accounts.
  226. #.......................................................................
  227. headers = ['Codbanca', 'Denumire', 'NrCont']
  228. headers2 = ['COD', 'DENUMIRE']
  229. short_name = self.winment_path + short_name
  230. nbanks = self.update_copy(short_name + '/nbanci.db')
  231. file_db = self.update_copy(short_name + db_file)
  232. bank_codes = {}
  233. with Table(file_db) as table, Table(nbanks) as nbanks:
  234. # tables = zip(table, nbanks)
  235. for bank in nbanks:
  236. data = [bank[header] for header in headers2]
  237. bdict = {data[i]: data[i + 1] for i in range(0, len(data), 2)}
  238. bank_codes.update(bdict)
  239. for row in table:
  240. if row.NrCont:
  241. bank_account = [row[header] for header in headers]
  242. bank_account.append(bank_codes[bank_account[0]])
  243. if bank_account[2].startswith(' '):
  244. yield bank_account
  245. def get_oblig(self, short_name, db_file='/ObligPI.DB'):
  246. oblig_headers = ['Part', 'TipDoc', 'Doc', 'NrDoc', 'DataDoc', 'Valoare', 'Rest']
  247. nparts_headres = ['Cod', 'Denumire', 'CodFiscal']
  248. short_name = self.winment_path + short_name
  249. nparts_db = self.update_copy(short_name + '/NPART.DB')
  250. oblig_db = self.update_copy(short_name + '/2023_02' + db_file)
  251. with Table(oblig_db) as table: # , Table(nbanks) as nbanks
  252. all_oblig = []
  253. parts = []
  254. tips = []
  255. docs = []
  256. nrs = []
  257. dates = []
  258. values = []
  259. rests = []
  260. for row in table:
  261. if row.TipDoc == 1 and row.Rest != 0:
  262. parts.append(row.Part)
  263. all_oblig.append(parts)
  264. tips.append(row.TipDoc)
  265. all_oblig.append(tips)
  266. docs.append(row.Doc)
  267. all_oblig.append(docs)
  268. nrs.append(row.NrDoc)
  269. all_oblig.append(nrs)
  270. dates.append(row.DataDoc)
  271. all_oblig.append(dates)
  272. values.append(row.Valoare)
  273. all_oblig.append(values)
  274. rests.append(row.Rest)
  275. all_oblig.append(rests)
  276. with Table(nparts_db) as table: # , Table(nbanks) as nbanks
  277. all_nparts = []
  278. cods = []
  279. names = []
  280. fiscals = []
  281. for row in table:
  282. # if row.TipDoc == 1:
  283. cods.append(row.Cod)
  284. all_nparts.append(cods)
  285. names.append(row.Denumire)
  286. all_nparts.append(names)
  287. fiscals.append(row.CodFiscal)
  288. all_nparts.append(fiscals)
  289. oblig_dict = dict(zip(oblig_headers, all_oblig))
  290. parts_dict = dict(zip(nparts_headres, all_nparts))
  291. return (oblig_dict, parts_dict)
  292. def corp_list(self, name=None):
  293. '''returns company list from actual shortnames from winmwnt/data folder
  294. '''
  295. dir_list = [f.name for f in os.scandir(self.winment_path) if f.is_dir() and '@' not in f.name]
  296. if name:
  297. if type(name) is list:
  298. for r in name:
  299. dir_list.remove(r)
  300. else:
  301. dir_list.remove(name)
  302. return dir_list
  303. def verif_corp(self, file_='/firme.db', ban=None):
  304. headers = ['Denumire', 'CF', 'J', 'Adresa', 'Oras', 'Judet', 'Prescurtat', 'Obs']
  305. corplist = self.make_list(self.winment_path + file_, headers) # dbRead.make_list(m_path + '/FIRME.DB', headers)
  306. if ban:
  307. if type(ban) is list:
  308. for r in corplist:
  309. for i in ban:
  310. if i == r[6]:
  311. corplist.remove(r)
  312. else:
  313. for r in corplist:
  314. if r == ban:
  315. corplist.remove(r)
  316. return corplist
  317. def verif_cont(self, file_) -> list:
  318. '''returns an account with its values from the balance
  319. :param file_ is shortname/ncont.db'''
  320. accounts = []
  321. headers = ['Clasa', 'Simbol', 'Denumire', 'SoldID', 'SoldIC', 'PrecedentD', 'PrecedentC', 'CurentD', 'CurentC']
  322. accountlist = self.make_list(self.winment_path + file_, headers) # + lunaCurenta
  323. for elem in accountlist:
  324. account = Account(clasa=elem[0],
  325. simbol=elem[1],
  326. denumire=elem[2],
  327. soldid=elem[3],
  328. soldic=elem[4],
  329. precedentd=elem[5],
  330. precedentc=elem[6],
  331. curentd=elem[7],
  332. curentc=elem[8])
  333. accounts.append(account)
  334. return accountlist
  335. def an_inc(self, account_list, boolind, ind2, ind3):
  336. '''returns the annual turnover in given year
  337. :param int boolind:account class number
  338. :param int ind2, ind3:debit or credit position in balance
  339. (6-rulaj curent debit, 8-rulaj cumulat debit, index starting from 0)'''
  340. tt = 0
  341. for account in account_list:
  342. # n = len(account[boolind]) == 3 and account[boolind] != '...' and int(account[boolind]) > 700 and int(account[boolind]) < 760
  343. n = account[boolind] == 7
  344. can = account[1][:2] != '76'
  345. # print(account[1][:2])
  346. if n and can:
  347. tt += int(account[ind2]) + int(account[ind3])
  348. return tt
  349. def divid(self, account_list, account='457') -> int:
  350. '''returns dividends/year
  351. :param account_list is account from ncont.db'''
  352. div_ = 0
  353. for acc in account_list:
  354. if acc[1][:3] == account:
  355. div_ += acc[8] + acc[6]
  356. return round(div_)
  357. def divid_current(self, account_list, account='457') -> int:
  358. '''returns dividends from current month
  359. :param account_list is account from ncont.db'''
  360. div_ = 0
  361. for acc in account_list:
  362. if acc[1][:3] == account:
  363. div_ += acc[8]
  364. return round(div_)
  365. def divid_intermed(self, account_list, account='463') -> int:
  366. '''returns dividends from current result/year
  367. :param account_list is account from ncont.db'''
  368. div_ = 0
  369. for acc in account_list:
  370. if acc[1][:3] == account:
  371. div_ += acc[7] + acc[5]
  372. return round(div_)
  373. def divid_inter_current(self, account_list, account='463') -> int:
  374. '''returns dividends from current results in the current month
  375. :param account_list is account from ncont.db'''
  376. div_ = 0
  377. for acc in account_list:
  378. if acc[1][:3] == account:
  379. div_ += acc[7]
  380. return round(div_)
  381. def spons(self, account_list, account='658.02') -> int:
  382. '''returns sponsored money/year
  383. :param account_list is account from ncont.db'''
  384. spons_ = 0
  385. for acc in account_list:
  386. if acc[1] == account:
  387. spons_ += acc[8] + acc[6]
  388. return round(spons_)
  389. def result(self, account_list, boolind, ind2, ind3) -> int: # account='121'
  390. '''returns the final result in given year
  391. :param int boolind:account class number
  392. :param int ind2, ind3:debit or credit position in balance
  393. (6-rulaj curent debit, 8-rulaj cumulat debit, index starting from 0)'''
  394. res_minus = res_plus = 0
  395. for r_minus in account_list:
  396. p = r_minus[boolind] == 6
  397. if p:
  398. res_minus += int(r_minus[ind2]) + int(r_minus[ind3])
  399. for r_plus in account_list:
  400. i = r_plus[boolind] == 7
  401. if i:
  402. res_plus += int(r_plus[ind2]) + int(r_plus[ind3])
  403. return res_plus - res_minus
  404. def ins_payable(self, account_list, account='431') -> int:
  405. '''returns insurences payable in current month
  406. :param account_list is account from ncont.db'''
  407. ins = 0
  408. for acc in account_list:
  409. p = acc[1][:3] == account
  410. if p:
  411. ins += acc[8]
  412. return round(ins)
  413. def CAS_payable(self, account_list, accounts=('431.02', '431.05')) -> int:
  414. '''returns CAS payable in current month
  415. :param account_list is account from ncont.db'''
  416. CAS = 0
  417. acc_1, acc_2 = accounts
  418. for acc in account_list:
  419. p = acc[1] == acc_1
  420. d = acc[1] == acc_2
  421. if p: CAS += acc[8]
  422. if d: CAS += acc[8]
  423. return round(CAS)
  424. def CASS_payable(self, account_list, accounts=('431.04', '431.06')) -> int:
  425. '''returns CASS payable in current month
  426. :param account_list is account from ncont.db'''
  427. CASS = 0
  428. acc_1, acc_2 = accounts
  429. for acc in account_list:
  430. p = acc[1] == acc_1
  431. d = acc[1] == acc_2
  432. if p: CASS += acc[8]
  433. if d: CASS += acc[8]
  434. return round(CASS)
  435. def sal_tax_payable(self, account_list, account='431.44') -> int:
  436. '''returns salary tax payable in current month
  437. :param account_list is account from ncont.db'''
  438. tax = 0
  439. for acc in account_list:
  440. p = acc[1] == account
  441. if p:
  442. tax += acc[8]
  443. return round(tax)
  444. def cam_payable(self, account_list, account='436') -> int:
  445. '''returns CAM payable in current month
  446. :param account_list is account from ncont.db'''
  447. cam = 0
  448. for acc in account_list:
  449. p = acc[1][:3] == account
  450. if p:
  451. cam += acc[8]
  452. return round(cam)
  453. def vat_payable(self, account_list, accounts=('442.03', '442.04')) -> int:
  454. '''returns VAT payable in current month
  455. :param account_list is account from ncont.db'''
  456. tt = 0
  457. acc_1, acc_2 = accounts
  458. for acc in account_list:
  459. p = acc[1] == acc_1
  460. d = acc[1] == acc_2
  461. if p:
  462. tt += acc[8]
  463. elif d:
  464. tt -= acc[7] + acc[8]
  465. return round(tt)
  466. def vat_final(self, acc_list, accounts=('442.03', '442.04')) -> int:
  467. '''returns VAT final payable in current month
  468. :param account_list is account from ncont.db'''
  469. tt = 0
  470. acc_1, acc_2 = accounts
  471. for acc in acc_list:
  472. account = Account(clasa=acc[0],
  473. simbol=acc[1],
  474. denumire=acc[2],
  475. soldid=acc[3],
  476. soldic=acc[4],
  477. precedentd=acc[5],
  478. precedentc=acc[6],
  479. curentd=acc[7],
  480. curentc=acc[8])
  481. payable = account.simbol == acc_1
  482. deductible = account.simbol == acc_2
  483. if payable:
  484. tt += account.soldf('c')
  485. elif deductible:
  486. tt -= account.soldf('d')
  487. return round(tt)
  488. def tax_payable(self, account_list, account='441') -> int:
  489. '''returns income TAX payable in current month
  490. :param account_list is account from ncont.db'''
  491. tax = 0
  492. for acc in account_list:
  493. p = acc[1][:3] == account
  494. if p:
  495. tax += acc[8]
  496. return round(tax)
  497. def div_tax_payable(self, acc_list, accounts='446.07') -> int:
  498. '''returns dividend TAX payable in current month
  499. :param account_list is account from ncont.db'''
  500. tt = 0
  501. # acc_1, acc_2 = accounts
  502. for acc in acc_list:
  503. account = Account(clasa=acc[0],
  504. simbol=acc[1],
  505. denumire=acc[2],
  506. soldid=acc[3],
  507. soldic=acc[4],
  508. precedentd=acc[5],
  509. precedentc=acc[6],
  510. curentd=acc[7],
  511. curentc=acc[8])
  512. payable = account.simbol == accounts
  513. # deductible = account.simbol == acc_2
  514. if payable:
  515. tt += account.curentc
  516. # elif deductible:
  517. # tt -= account.soldf('d')
  518. return round(tt)
  519. def advance_final(self, acc_list, accounts='542') -> int:
  520. '''returns final dvances/year
  521. :param account_list is account from ncont.db'''
  522. tt = 0
  523. # acc_1, acc_2 = accounts
  524. for acc in acc_list:
  525. account = Account(clasa=acc[0],
  526. simbol=acc[1],
  527. denumire=acc[2],
  528. soldid=acc[3],
  529. soldic=acc[4],
  530. precedentd=acc[5],
  531. precedentc=acc[6],
  532. curentd=acc[7],
  533. curentc=acc[8])
  534. payable = account.simbol[:3] == accounts
  535. # deductible = account.simbol == acc_2
  536. if payable:
  537. tt += account.soldf('d')
  538. # elif deductible:
  539. # tt -= account.soldf('d')
  540. return round(tt)
  541. def deb_div(self, acc_list, accounts='461') -> int:
  542. '''returns advances transfered to deb. diversi/year
  543. :param account_list is account from ncont.db'''
  544. tt = 0
  545. # acc_1, acc_2 = accounts
  546. for acc in acc_list:
  547. account = Account(clasa=acc[0],
  548. simbol=acc[1],
  549. denumire=acc[2],
  550. soldid=acc[3],
  551. soldic=acc[4],
  552. precedentd=acc[5],
  553. precedentc=acc[6],
  554. curentd=acc[7],
  555. curentc=acc[8])
  556. payable = account.simbol[:3] == accounts
  557. # deductible = account.simbol == acc_2
  558. if payable:
  559. tt += account.soldf('d')
  560. # elif deductible:
  561. # tt -= account.soldf('d')
  562. return round(tt)
  563. def credit_final(self, acc_list, accounts='455') -> int:
  564. '''returns credited ammount/year
  565. :param account_list is account from ncont.db'''
  566. tt = 0
  567. # acc_1, acc_2 = accounts
  568. for acc in acc_list:
  569. account = Account(clasa=acc[0],
  570. simbol=acc[1],
  571. denumire=acc[2],
  572. soldid=acc[3],
  573. soldic=acc[4],
  574. precedentd=acc[5],
  575. precedentc=acc[6],
  576. curentd=acc[7],
  577. curentc=acc[8])
  578. payable = account.simbol[:3] == accounts
  579. # deductible = account.simbol == acc_2
  580. if payable:
  581. tt += account.soldf('d')
  582. # elif deductible:
  583. # tt -= account.soldf('d')
  584. return round(tt)
  585. if __name__ == '__main__':
  586. mentor = WinMentor()
  587. # accounts = list(mentor.get_bank_accounts('WEBS'))
  588. # account_num = [n for n in accounts[1] if n.startswith(' ')]
  589. for account in mentor.get_bank_accounts('WEBS'):
  590. # if account[2].startswith(' '):
  591. print(account)