dolibarrAlchemyHledger.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import settings
  4. from himports.dolibarrAlchemy import *
  5. class HledgerEntry(object):
  6. accounting_years = settings.get('ACCOUNTING_YEARS')
  7. pc_default_tiers = settings.get('PC_REFS')['default_tiers']
  8. pc_default_client = settings.get('PC_REFS')['default_client']
  9. pc_default_supplier = settings.get('PC_REFS')['default_supplier']
  10. pc_default_produit = settings.get('PC_REFS')['default_produit']
  11. pc_default_charge = settings.get('PC_REFS')['default_charge']
  12. pc_default_bank = settings.get('PC_REFS')['default_bank']
  13. tva_type = settings.get('TVA_TYPE')
  14. sql_class = None
  15. # Date permettant de déterminer dans quel exercice comptable
  16. # l'écriture doit se trouver
  17. k_accounting_date = None
  18. def __init__(self, e):
  19. super(HledgerEntry, self).__init__()
  20. self.e = e
  21. self.accounting_date = e
  22. for attr in self.k_accounting_date.split('.'):
  23. self.accounting_date = getattr(self.accounting_date, attr)
  24. @classmethod
  25. def get_entries(cls, session):
  26. return [cls(i) for i in session.query(cls.sql_class).all()]
  27. def get_ledger(self):
  28. print "WARNING: get_ledger not done"
  29. return u""
  30. def check_pc(self):
  31. return ()
  32. def get_year(self):
  33. raise Exception("TODO: get_year not implemented for class %s" % (self.__class__))
  34. def get_accounting_year(self):
  35. date = self.accounting_date
  36. if isinstance(date, datetime.datetime):
  37. date = date.date()
  38. for (year, dbegin, dend) in HledgerEntry.accounting_years:
  39. if date >= dbegin and date <= dend:
  40. return year
  41. return str(date.year)
  42. class HledgerJournal(object):
  43. def __init__(self, session, cls_entry):
  44. self.entries = cls_entry.get_entries(session)
  45. def get_entries(self):
  46. return self.entries
  47. def get_by_year(self):
  48. by_year = {}
  49. for entry in self.get_entries():
  50. entry_year = entry.get_accounting_year()
  51. if entry_year not in by_year:
  52. by_year[entry_year] = []
  53. by_year[entry_year].append(entry)
  54. return by_year
  55. def check_pc(self):
  56. pc_missing = set()
  57. for entry in self.get_entries():
  58. pc_missing.update(entry.check_pc())
  59. return pc_missing
  60. class HledgerBankEntry(HledgerEntry):
  61. sql_class = Bank
  62. k_accounting_date = 'datev'
  63. @classmethod
  64. def get_third_code(cls, e):
  65. third_code = ""
  66. if e.url_payment_supplier:
  67. if e.url_company:
  68. third_code = e.url_company.societe.code_compta_fournisseur
  69. if e.url_payment_sc:
  70. code = e.url_payment_sc.payment_sc.cotisation_sociale.type.code
  71. if code in settings.get('SOCIAL_REFS'):
  72. third_code = settings.get('SOCIAL_REFS')[code]
  73. if e.url_payment:
  74. if e.url_company:
  75. third_code = e.url_company.societe.code_compta
  76. if third_code == "":
  77. fn = settings.get('PC_REFS')['fn_custom_code']
  78. third_code = fn(e)
  79. if third_code == "":
  80. third_code = cls.pc_default_tiers
  81. return third_code
  82. @classmethod
  83. def get_description(self, e):
  84. s_nom = ""
  85. s_description = ""
  86. if e.url_company:
  87. s_nom = e.url_company.societe.nom
  88. if e.url_payment_supplier:
  89. f_ids = [f.facture.ref_supplier for f in e.url_payment_supplier.payment_supplier.factures]
  90. s_description = "Règlement facture fournisseur - %s - %s" % (
  91. s_nom,
  92. "|".join(f_ids),
  93. )
  94. if e.url_payment:
  95. f_ids = [f.facture.facnumber for f in e.url_payment.payment.factures]
  96. s_description = "Règlement facture client - %s - %s" % (
  97. s_nom,
  98. "|".join(f_ids),
  99. )
  100. if s_description == "":
  101. s_description = s_nom + " - " + e.label
  102. return s_description
  103. def get_ledger(self):
  104. e = self.e
  105. s = ""
  106. s_description = self.get_description(self.e)
  107. s += "%(date)s %(description)s\n" % {
  108. 'date': e.datev.strftime("%Y/%m/%d"),
  109. 'description': s_description
  110. }
  111. third_code = self.get_third_code(self.e)
  112. ba_code = e.account.account_number
  113. if ba_code == "":
  114. ba_code = self.pc_default_bank
  115. s += " %(account)s \t %(amount)s\n" % {
  116. 'account': settings.get_ledger_account(ba_code),
  117. 'amount': -e.amount
  118. }
  119. s += " %(account)s \t %(amount)s\n" % {
  120. 'account': settings.get_ledger_account(third_code),
  121. 'amount': e.amount
  122. }
  123. if e.url_payment_supplier:
  124. for f in e.url_payment_supplier.payment_supplier.factures:
  125. tvas = HledgerSupplierEntry.get_tva_paiement_amounts(f.facture, journal="bank")
  126. for k in tvas:
  127. s += " %(account_tva)s \t %(amount_tva)s\n" % {
  128. 'account_tva': settings.get_ledger_account(k),
  129. 'amount_tva': tvas[k]
  130. }
  131. elif e.url_payment:
  132. for f in e.url_payment.payment.factures:
  133. tvas = HledgerSellEntry.get_tva_paiement_amounts(f.facture, journal="bank")
  134. for k in tvas:
  135. s += " %(account_tva)s \t %(amount_tva)s\n" % {
  136. 'account_tva': settings.get_ledger_account(k),
  137. 'amount_tva': tvas[k]
  138. }
  139. else:
  140. pass
  141. return s
  142. @classmethod
  143. def get_entries(cls, session):
  144. return [cls(e) for e in session.query(cls.sql_class).order_by(Bank.datev, Bank.num_releve).all()]
  145. class HledgerFactureEntry(HledgerEntry):
  146. @classmethod
  147. def get_entries(cls, session):
  148. return [cls(e) for e in session.query(cls.sql_class).order_by(cls.sql_class.datef).all()]
  149. @classmethod
  150. def is_tva_facture(cls, ed):
  151. return ed.productcls.tva_type == 'service_sur_debit' and ed.product_type == 1
  152. @classmethod
  153. def is_tva_paiement(cls, ed):
  154. return cls.tva_type != 'service_sur_debit' or ed.product_type != 1
  155. @classmethod
  156. def get_tva_amounts(cls, e, journal):
  157. tvas = dict()
  158. for ed in e.details:
  159. if isinstance(e, Facture):
  160. total_tva = -ed.total_tva
  161. elif isinstance(e, FactureFourn):
  162. total_tva = ed.tva
  163. else:
  164. raise Exception("Should be either Facture or FactureFourn")
  165. if total_tva == 0:
  166. continue
  167. tva_account = cls.get_tva_account(ed)
  168. tva_regul = cls.get_tva_regul_account(ed)
  169. if journal == "bank":
  170. if ed.product_type == 1 and cls.tva_type == 'standard':
  171. if tva_regul not in tvas:
  172. tvas[tva_regul] = 0
  173. if tva_account not in tvas:
  174. tvas[tva_account] = 0
  175. tvas[tva_account] += -total_tva
  176. tvas[tva_regul] += total_tva
  177. elif journal == "sell" or journal == "supplier":
  178. if ed.product_type == 1 and cls.tva_type == 'standard':
  179. if tva_regul not in tvas:
  180. tvas[tva_regul] = 0
  181. tvas[tva_regul] += -total_tva
  182. else:
  183. if tva_account not in tvas:
  184. tvas[tva_account] = 0
  185. tvas[tva_account] += -total_tva
  186. return tvas
  187. @classmethod
  188. def get_tva_regul_account(cls, ed):
  189. return settings.get('PC_REFS')['tva_regul']
  190. # Calcul de la tva à décaisser à paiement de la facture
  191. #
  192. # Cela du type de produit (bien ou service) et du type de tva
  193. @classmethod
  194. def get_tva_facture_amounts(cls, e, journal):
  195. return cls.get_tva_amounts(e, journal)
  196. @classmethod
  197. def get_tva_paiement_amounts(cls, e, journal):
  198. return cls.get_tva_amounts(e, journal)
  199. class HledgerSupplierEntry(HledgerFactureEntry):
  200. sql_class = FactureFourn
  201. k_accounting_date = 'datef'
  202. def check(self):
  203. e = self.e
  204. total_ttc = e.total_ttc
  205. total_tva = e.total_tva
  206. total_ht = e.total_ht
  207. for ed in e.details:
  208. total_ttc -= ed.total_ttc
  209. total_tva -= ed.tva
  210. total_ht -= ed.total_ht
  211. if total_ttc > 1e-10:
  212. print "Erreur dans l'écriture %s: total ttc = %s" % (e.ref_supplier, total_ttc)
  213. if total_ht > 1e-10:
  214. print "Erreur dans l'écriture %s: total ht = %s" % (e.ref_supplier, total_ht)
  215. if total_tva > 1e-10:
  216. print "Erreur dans l'écriture %s: total tva = %s" % (e.ref_supplier, total_tva)
  217. def getMissingPC(self):
  218. pc_missing = []
  219. if e.societe.code_compta_fournisseur == "":
  220. pc_missing.append("tiers:fournisseur: %s %s" % (e.societe.nom, s.societe.ape))
  221. for ed in e.details:
  222. if self.get_product_account_code(ed) == self.pc_default_charge:
  223. pc_missing.append("achat: %s - %s" % (e.ref_supplier, ed.description.splitlines()[0]))
  224. return pc_missing
  225. def get_ledger(self):
  226. e = self.e
  227. self.check()
  228. s = ""
  229. s += "%(date)s %(description)s\n" % {
  230. 'date': e.datef.strftime("%Y/%m/%d"),
  231. 'description': e.ref_supplier + " - " + e.societe.nom,
  232. }
  233. s_code = self.get_supplier_code(self.e)
  234. s += " %(compte_tiers)s %(amount_ttc)s\n" % {
  235. 'compte_tiers': settings.get_ledger_account(s_code),
  236. 'amount_ttc': e.total_ttc,
  237. }
  238. for ed in e.details:
  239. p_code = self.get_product_account_code(ed)
  240. s += " %(compte_produit)s %(amount_ht)s\n" % {
  241. 'compte_produit': settings.get_ledger_account(p_code),
  242. 'amount_ht': -ed.total_ht
  243. }
  244. tvas = self.get_tva_facture_amounts(self.e, journal="supplier")
  245. for k in tvas:
  246. s += " %(compte_tva)s %(amount_tva)s\n" % {
  247. 'compte_tva': settings.get_ledger_account(k),
  248. 'amount_tva': tvas[k],
  249. }
  250. return s
  251. @classmethod
  252. def get_tva_account(cls, ed):
  253. p_code = cls.get_product_account_code(ed)
  254. if p_code.startswith("2"):
  255. tva_account = settings.get('PC_REFS')['tva_deductible']
  256. else:
  257. tva_account = settings.get('PC_REFS')['tva_deductible_immo']
  258. return tva_account
  259. @classmethod
  260. def get_product_account_code(cls, ed):
  261. p_code = ""
  262. if ed.accounting_account:
  263. p_code = ed.accounting_account.account_number
  264. elif ed.product:
  265. p_code = ed.product.accountancy_code_buy
  266. else:
  267. p_code = cls.pc_default_charge
  268. return p_code
  269. @classmethod
  270. def get_supplier_code(cls, e):
  271. s_code = e.societe.code_compta_fournisseur
  272. if s_code == "":
  273. s_code = cls.pc_default_supplier
  274. return s_code
  275. class HledgerSellEntry(HledgerFactureEntry):
  276. sql_class = Facture
  277. k_accounting_date = 'datef'
  278. def get_ledger(self):
  279. e = self.e
  280. self.check()
  281. s = ""
  282. # Date et description
  283. s += "%(date)s %(description)s\n" % {
  284. 'date': e.datef.strftime("%Y/%m/%d"),
  285. 'description': e.facnumber + " - " + e.societe.nom,
  286. }
  287. # ligne pour compte client
  288. s_code = self.get_client_code(self.e)
  289. s += " %(compte_tiers)s %(amount_ttc)s\n" % {
  290. 'compte_tiers': settings.get_ledger_account(s_code),
  291. 'amount_ttc': -e.total_ttc,
  292. }
  293. # lignes pour compte de produits
  294. for ed in e.details:
  295. p_code = self.get_product_account_code(ed)
  296. s += " %(compte_produit)s %(amount_ht)s\n" % {
  297. 'compte_produit': settings.get_ledger_account(p_code),
  298. 'amount_ht': ed.total_ht
  299. }
  300. # lignes pour la tva
  301. tvas = self.get_tva_facture_amounts(self.e, journal="sell")
  302. for k in tvas:
  303. s += " %(compte_tva)s %(amount_tva)s\n" % {
  304. 'compte_tva': settings.get_ledger_account(k),
  305. 'amount_tva': tvas[k],
  306. }
  307. return s
  308. @classmethod
  309. def get_tva_account(cls, ed):
  310. return settings.get('PC_REFS')['tva_collecte']
  311. def getMissingPC(self):
  312. e = self.e
  313. pc_missing = []
  314. if e.societe.code_compta == "":
  315. pc_missing.append("tiers: %s %s" % (e.societe.nom, s.societe.ape))
  316. for ed in e.details:
  317. if self.get_product_account_code(ed) == self.pc_default_produit:
  318. if ed.description != "":
  319. description = ed.description.splitlines()[0]
  320. else:
  321. description = ed.description
  322. pc_missing.append("produit: %s - %s - %s" % (e.societe.nom, e.facnumber, description))
  323. return pc_missing
  324. @classmethod
  325. def get_product_account_code(cls, ed):
  326. p_code = ""
  327. if ed.accounting_account:
  328. p_code = ed.accounting_account.account_number
  329. elif ed.product:
  330. p_code = ed.product.accountancy_code_sell
  331. else:
  332. p_code = cls.pc_default_produit
  333. return p_code
  334. @classmethod
  335. def get_client_code(cls, e):
  336. s_code = e.societe.code_compta
  337. if s_code == "":
  338. s_code = cls.pc_default_client
  339. return s_code
  340. def check(self):
  341. e = self.e
  342. total_ttc = e.total_ttc
  343. total_tva = e.tva
  344. total_ht = e.total
  345. for ed in e.details:
  346. total_ttc -= ed.total_ttc
  347. total_tva -= ed.total_tva
  348. total_ht -= ed.total_ht
  349. if total_ttc > 1e-10:
  350. print "Erreur dans l'écriture %s: total ttc = %s" % (e.facnumber, total_ttc)
  351. if total_ht > 1e-10:
  352. print "Erreur dans l'écriture %s: total ht = %s" % (e.facnumber, total_ht)
  353. if total_tva > 1e-10:
  354. print "Erreur dans l'écriture %s: total tva = %s" % (e.facnumber, total_tva)
  355. class HledgerSocialEntry(HledgerEntry):
  356. sql_class = CotisationsSociales
  357. k_accounting_date = 'date_ech'
  358. @classmethod
  359. def get_entries(cls, session):
  360. return [cls(e) for e in session.query(cls.sql_class).order_by(CotisationsSociales.date_ech).all()]
  361. @classmethod
  362. def get_third_code(cls, e):
  363. third_code = ""
  364. if e.type.code in settings.get('SOCIAL_REFS'):
  365. third_code = settings.get('SOCIAL_REFS')[e.type.code]
  366. if third_code == "":
  367. third_code = cls.pc_default_supplier
  368. return third_code
  369. @classmethod
  370. def get_social_code(cls, e):
  371. s_code = ""
  372. if e.type:
  373. s_code = e.type.accountancy_code
  374. if s_code == "":
  375. s_code = cls.pc_default_charge
  376. return s_code
  377. def getMissingPC(self):
  378. pc_missing = []
  379. if self.get_social_code(self.e) == self.pc_default_charge:
  380. pc_missing.append("charges: %s" % (e.libelle))
  381. if self.get_third_code(self.e) == self.pc_default_supplier:
  382. pc_missing.append("tiers: %s (%s)" % (e.libelle, e.type.code))
  383. return pc_missing
  384. def get_ledger(self):
  385. e = self.e
  386. s = ""
  387. s += "%(date)s %(description)s\n" % {
  388. 'date': e.date_ech.strftime("%Y/%m/%d"),
  389. 'description': e.libelle + " - " + e.type.libelle
  390. }
  391. third_code = self.get_third_code(self.e)
  392. s_code = self.get_social_code(self.e)
  393. s += " %(account)s \t %(amount)s\n" % {
  394. 'account': settings.get_ledger_account(third_code),
  395. 'amount': e.amount
  396. }
  397. s += " %(account)s \t %(amount)s\n" % {
  398. 'account': settings.get_ledger_account(s_code),
  399. 'amount': -e.amount
  400. }
  401. return s
  402. def check(self):
  403. e = self.e
  404. class HledgerDolibarrSQLAlchemy(DolibarrSQLAlchemy):
  405. def get_bank_journal(self):
  406. return HledgerJournal(self.session, HledgerBankEntry)
  407. def get_supplier_journal(self):
  408. return HledgerJournal(self.session, HledgerSupplierEntry)
  409. def get_sell_journal(self):
  410. return HledgerJournal(self.session, HledgerSellEntry)
  411. def get_social_journal(self):
  412. return HledgerJournal(self.session, HledgerSocialEntry)