diff --git a/netforce_clinic/models/__init__.py b/netforce_clinic/models/__init__.py index 7c9cbfa..9e9cc5f 100644 --- a/netforce_clinic/models/__init__.py +++ b/netforce_clinic/models/__init__.py @@ -142,3 +142,4 @@ from . import print_labor_cost from . import print_labor_cost_line from . import conv_bal from . import conv_sale_invoice +from . import account_move_line diff --git a/netforce_clinic/models/account_move_line.py b/netforce_clinic/models/account_move_line.py new file mode 100644 index 0000000..96c24a6 --- /dev/null +++ b/netforce_clinic/models/account_move_line.py @@ -0,0 +1,7 @@ +from netforce.model import Model + +class AccountMoveLine(Model): + _inherit="account.move.line" + + +AccountMoveLine.register() diff --git a/netforce_clinic/models/account_payment.py b/netforce_clinic/models/account_payment.py index e8f1172..6fd9236 100644 --- a/netforce_clinic/models/account_payment.py +++ b/netforce_clinic/models/account_payment.py @@ -1,3 +1,6 @@ +from pprint import pprint + + from netforce.model import Model, fields, get_model class AccountPayment(Model): @@ -58,9 +61,447 @@ class AccountPayment(Model): } } + def clinic_post(self,ids,context={}): + print("account_payment.post") + obj=self.browse(ids)[0] + settings=get_model("settings").browse(1) + if obj.currency_rate: + currency_rate=obj.currency_rate + else: + if obj.currency_id.id==settings.currency_id.id: + currency_rate=1.0 + else: + rate_from=obj.currency_id.get_rate(date=obj.date) + if not rate_from: + raise Exception("Missing currency rate for %s"%obj.currency_id.code) + rate_to=settings.currency_id.get_rate(date=obj.date) + if not rate_to: + raise Exception("Missing currency rate for %s"%settings.currency_id.code) + currency_rate=rate_from/(rate_to or 1) + obj.write({"currency_rate":currency_rate}) + if obj.pay_type=="direct": + desc=obj.memo or obj.ref or obj.partner_id.name or obj.number # XXX: as in myob? + elif obj.pay_type=="invoice": + desc=obj.memo or "Payment; %s"%obj.partner_id.name # XXX: as in myob? + elif obj.pay_type=="prepay": + desc="Prepayment: %s"%obj.partner_id.name + elif obj.pay_type=="overpay": + desc="Overpayment: %s"%obj.partner_id.name + elif obj.pay_type=="refund": + desc="Refund: %s"%obj.partner_id.name + elif obj.pay_type=="claim": + desc="Expense claim payment" + elif obj.pay_type=="adjust": + desc="Adjustment" + else: + desc="Payment: %s"%obj.partner_id.name + if obj.type=="in": + journal_id=settings.pay_in_journal_id.id + if not journal_id: + raise Exception("Receipts journal not found") + elif obj.type=="out": + journal_id=settings.pay_out_journal_id.id + if not journal_id: + raise Exception("Disbursements journal not found") + if not obj.number: + raise Exception("Missing payment number") + move_vals={ + "journal_id": journal_id, + "number": obj.number, + "date": obj.date, + "narration": desc, + "related_id": "account.payment,%s"%obj.id, + "company_id": obj.company_id.id, + } + move_id=get_model("account.move").create(move_vals) + lines=[] + track_id=None + for line in obj.lines: # XXX + if line.track_id: + if track_id: + track_id=None + break + else: + track_id=line.track_id.id + amt=get_model("currency").convert(obj.amount_payment,obj.currency_id.id,settings.currency_id.id,rate=currency_rate) + if obj.type=="out": + amt=-amt + line_vals={ + "move_id": move_id, + "account_id": obj.account_id.id, + "description": desc, + "track_id": track_id, + "debit": amt>0 and amt or 0, + "credit": amt<0 and -amt or 0, + } + if obj.account_id.currency_id.id!=settings.currency_id.id: + if obj.account_id.currency_id.id!=obj.currency_id.id: + raise Exception("Invalid account currency for this payment: %s"%obj.account_id.code) + line_vals["amount_cur"]=obj.amount_payment if obj.type=="in" else -obj.amount_payment + get_model("account.move.line").create(line_vals) + taxes={} + reconcile_ids=[] + total_over=0 + for line in obj.lines: + if line.type in ("direct","prepay"): + cur_amt=get_model("currency").convert(line.amount,obj.currency_id.id,settings.currency_id.id,rate=currency_rate) + tax=line.tax_id + if tax and obj.tax_type!="no_tax": + base_amt=get_model("account.tax.rate").compute_base(tax.id,cur_amt,tax_type=obj.tax_type) + tax_comps=get_model("account.tax.rate").compute_taxes(tax.id,base_amt,when="direct_payment") + for comp_id,tax_amt in tax_comps.items(): + if comp_id in taxes: + tax_vals=taxes[comp_id] + tax_vals["amount_base"]+=base_amt + tax_vals["amount_tax"]+=tax_amt + else: + tax_vals={ + "tax_comp_id": comp_id, + "amount_base": base_amt, + "amount_tax": tax_amt, + } + taxes[comp_id]=tax_vals + else: + base_amt=cur_amt + if obj.type=="out": + amt=base_amt + else: + amt=-base_amt + line_vals={ + "move_id": move_id, + "description": line.description or desc, + "account_id": line.account_id.id, + "debit": amt>0 and amt or 0, + "credit": amt<0 and -amt or 0, + "track_id": line.track_id.id, + "track2_id": line.track2_id.id, + } + print("direct") + pprint(line_vals) + get_model("account.move.line").create(line_vals) + elif line.type in ("invoice","refund"): + inv=line.invoice_id + inv_taxes={} + if inv.inv_type in ("invoice","credit","debit"): + line_vals={ + "move_id": move_id, + "description": desc, + "account_id": inv.account_id.id, + "due_date": inv.due_date, + "partner_id": inv.partner_id.id, + } + if line.amount_currency>inv.amount_due: + pay_amt=inv.amount_due + over_amt=line.amount_currency-inv.amount_due + total_over+=get_model("currency").convert(over_amt,inv.currency_id.id,settings.currency_id.id,rate=inv.currency_rate) + else: + pay_amt=line.amount_currency + pay_ratio=pay_amt/inv.amount_total + inv_amt=inv.amount_total*pay_ratio + cur_inv_amt=get_model("currency").convert(inv_amt,inv.currency_id.id,settings.currency_id.id,rate=inv.currency_rate) + if obj.type=="out": + amt=cur_inv_amt + else: + amt=-cur_inv_amt + if amt>0: + line_vals["debit"]=amt + else: + line_vals["credit"]=-amt + if inv.account_id.currency_id.id!=settings.currency_id.id: + if obj.type=="out": + line_vals["amount_cur"]=inv_amt + else: + line_vals["amount_cur"]=-inv_amt + print("invoice") + pprint(line_vals) + pay_line_id=get_model("account.move.line").create(line_vals) + if inv.reconcile_move_line_id: + inv_line_id=inv.reconcile_move_line_id.id + elif inv.move_id: # XXX + inv_line_id=inv.move_id.lines[0].id + else: + inv_line_id=None + if inv_line_id: + #customize + reconcile_ids.append([pay_line_id,inv_line_id]) + for invline in inv.lines: + tax=invline.tax_id + if tax and inv.tax_type!="no_tax": # XXX: simplify this + cur_line_amt_inv=get_model("currency").convert(invline.amount*pay_ratio,inv.currency_id.id,settings.currency_id.id,rate=inv.currency_rate) + base_amt=get_model("account.tax.rate").compute_base(tax.id,cur_line_amt_inv,tax_type=inv.tax_type) + tax_comps=get_model("account.tax.rate").compute_taxes(tax.id,base_amt,when="invoice_payment_inv") + for comp_id,tax_amt in tax_comps.items(): + if comp_id in inv_taxes: + tax_vals=inv_taxes[comp_id] + tax_vals["amount_base"]+=base_amt + tax_vals["amount_tax"]+=tax_amt + else: + tax_vals={ + "tax_comp_id": comp_id, + "amount_base": base_amt, + "amount_tax": tax_amt, + } + inv_taxes[comp_id]=tax_vals + cur_line_amt_pmt=get_model("currency").convert(invline.amount*pay_ratio,inv.currency_id.id,settings.currency_id.id,rate=inv.currency_rate) # XXX: check this + base_amt=get_model("account.tax.rate").compute_base(tax.id,cur_line_amt_pmt,tax_type=inv.tax_type) + tax_comps=get_model("account.tax.rate").compute_taxes(tax.id,base_amt,when="invoice_payment_pmt") + for comp_id,tax_amt in tax_comps.items(): + if comp_id in inv_taxes: + tax_vals=inv_taxes[comp_id] + tax_vals["amount_base"]+=base_amt + tax_vals["amount_tax"]+=tax_amt + else: + tax_vals={ + "tax_comp_id": comp_id, + "amount_base": base_amt, + "amount_tax": tax_amt, + } + inv_taxes[comp_id]=tax_vals + elif inv.inv_type=="overpay": + line_vals={ + "move_id": move_id, + "description": desc, + "account_id": inv.account_id.id, + "partner_id": inv.partner_id.id, + } + amt=line.amount + if obj.type=="out": + line_vals["debit"]=amt + else: + line_vals["credit"]=amt + print("overpay") + pprint(line_vals) + get_model("account.move.line").create(line_vals) + elif inv.inv_type=="prepay": + for oline in inv.lines: + line_vals={ + "move_id": move_id, + "description": desc, + "account_id": oline.account_id.id, + } + amt=oline.amount*line.amount/inv.amount_total # XXX: currency + tax=oline.tax_id + if tax: + base_amt=get_model("account.tax.rate").compute_base(tax.id,amt,tax_type=inv.tax_type) + else: + base_amt=amt + if obj.type=="out": + line_vals["debit"]=base_amt + else: + line_vals["credit"]=base_amt + print("prepay") + pprint(line_vals) + get_model("account.move.line").create(line_vals) + if tax and inv.tax_type!="no_tax": + tax_comps=get_model("account.tax.rate").compute_taxes(tax.id,base_amt,when="invoice") # XXX + for comp_id,tax_amt in tax_comps.items(): + if comp_id in inv_taxes: + tax_vals=inv_taxes[comp_id] + tax_vals["amount_base"]+=base_amt + tax_vals["amount_tax"]+=tax_amt + else: + tax_vals={ + "tax_comp_id": comp_id, + "amount_base": base_amt, + "amount_tax": tax_amt, + } + inv_taxes[comp_id]=tax_vals + for comp_id,inv_tax_vals in inv_taxes.items(): + comp=get_model("account.tax.component").browse(comp_id) + if comp.type in ("vat","vat_defer"): + acc_id=comp.account_id.id + if not acc_id: + raise Exception("Missing account for tax component %s"%comp.name) + line_vals={ + "move_id": move_id, + "description": desc, + "account_id": acc_id, + "tax_comp_id": comp_id, + "tax_base": inv_tax_vals["amount_base"], + "partner_id": obj.partner_id.id, + "invoice_id": inv.id, + } + if comp.type=="vat": + if inv.type=="out": + if line.tax_no: + tax_no=line.tax_no + else: + tax_no=get_model("account.invoice").gen_tax_no(context={"date":obj.date}) + line.write({"tax_no":tax_no}) + line_vals["tax_no"]=tax_no + elif inv.type=="in": + line_vals["tax_no"]=line.tax_no + amt=inv_tax_vals["amount_tax"] + if obj.type=="in": + amt=-amt + if amt>0: + line_vals["debit"]=amt + else: + line_vals["credit"]=-amt + print("tax") + pprint(line_vals) + get_model("account.move.line").create(line_vals) + elif comp.type=="wht": + if comp_id in taxes: + tax_vals=taxes[comp_id] + tax_vals["amount_base"]+=inv_tax_vals["amount_base"] + tax_vals["amount_tax"]+=inv_tax_vals["amount_tax"] + else: + taxes[comp_id]=inv_tax_vals.copy() + elif line.type=="claim": + expense=line.expense_id + line_vals={ + "move_id": move_id, + "description": desc, + "account_id": settings.unpaid_claim_id.id, + } + amt=line.amount + if obj.type=="out": + line_vals["debit"]=amt + else: + line_vals["credit"]=amt + print("claim") + pprint(line_vals) + get_model("account.move.line").create(line_vals) + elif line.type=="adjust": + cur_amt=get_model("currency").convert(line.amount,obj.currency_id.id,settings.currency_id.id,rate=currency_rate) + tax_base=get_model("currency").convert(line.tax_base or 0,obj.currency_id.id,settings.currency_id.id,rate=currency_rate) + line_vals={ + "move_id": move_id, + "description": desc, + "account_id": line.account_id.id, + "tax_comp_id": line.tax_comp_id.id, + "tax_base": tax_base, + "track_id": line.track_id.id, + "partner_id": obj.partner_id.id, + } + if obj.type=="in": + cur_amt=-cur_amt + if cur_amt>0: + line_vals["debit"]=cur_amt + else: + line_vals["credit"]=-cur_amt + print("adjust") + pprint(line_vals) + get_model("account.move.line").create(line_vals) + if total_over>0.01: + partner=obj.partner_id + if obj.type=="in": + account_id=partner.account_receivable_id.id or settings.account_receivable_id.id + if not account_id: + raise Exception("Account receivable not found") + elif obj.type=="out": + account_id=partner.account_payable_id.id or settings.account_payable_id.id + if not account_id: + raise Exception("Account payable not found") + line_vals={ + "move_id": move_id, + "description": context.get("overpay_description",""), + "account_id": account_id, + "track_id": line.track_id.id, + "partner_id": obj.partner_id.id, + } + if obj.type=="out": + line_vals["debit"]=total_over + else: + line_vals["credit"]=total_over + print("overpay") + pprint(line_vals) + get_model("account.move.line").create(line_vals) + inv_line_vals={ + "description": context.get("overpay_description",""), + "account_id": account_id, + "amount": total_over, + } + inv_vals={ + "type": obj.type=="in" and "out" or "in", + "inv_type": "overpay", + "partner_id": obj.partner_id.id, + "date": obj.date, + "tax_type": "no_tax", + "lines": [("create",inv_line_vals)], + "state": "waiting_payment", + "payment_id": obj.id, + "account_id": account_id, + } + inv_id=get_model("account.invoice").create(inv_vals) + wht_no=get_model("account.payment").gen_wht_no(context={"date":obj.date}) + for comp_id,tax_vals in sorted(taxes.items()): + comp=get_model("account.tax.component").browse(comp_id) + acc_id=comp.account_id.id + if not acc_id: + raise Exception("Missing account for tax component %s"%comp.name) + line_vals={ + "move_id": move_id, + "description": desc, + "account_id": acc_id, + "tax_comp_id": comp_id, + "tax_base": tax_vals["amount_base"], + "partner_id": obj.partner_id.id, + } + if comp.type=="vat": + if obj.type=="in": + if obj.tax_no: + tax_no=obj.tax_no + else: + tax_no=get_model("account.invoice").gen_tax_no(context={"date":obj.date}) + obj.write({"tax_no":tax_no}) + line_vals["tax_no"]=tax_no + elif obj.type=="out": + line_vals["tax_no"]=obj.tax_no + elif comp.type=="wht": + if obj.type=="out": + # 1 Payment should have same wht_no + #wht_no=get_model("account.payment").gen_wht_no(context={"date":obj.date}) + line_vals["tax_no"]=wht_no + amt=tax_vals["amount_tax"] + if obj.type=="in": + amt=-amt + if amt>0: + line_vals["debit"]=amt + else: + line_vals["credit"]=-amt + print("tax") + pprint(line_vals) + get_model("account.move.line").create(line_vals) + amt=0 + move=get_model("account.move").browse(move_id) + for line in move.lines: + amt+=line.credit-line.debit + if amt>0.001: + if not settings.currency_loss_id: + raise Exception("Missing currency loss account") + line_vals={ + "move_id": move_id, + "description": desc, + "account_id": settings.currency_loss_id.id, + "debit": amt, + "credit": 0, + } + get_model("account.move.line").create(line_vals) + elif amt<-0.001: + if not settings.currency_gain_id: + raise Exception("Missing currency gain account") + line_vals={ + "move_id": move_id, + "description": desc, + "account_id": settings.currency_loss_id.id, + "debit": 0, + "credit": -amt, + } + get_model("account.move.line").create(line_vals) + get_model("account.move").post([move_id]) + obj.write({"move_id":move_id,"state":"posted"}) + #Cutomize + for rec_lines in reconcile_ids: + get_model("account.move.line").reconcile(rec_lines) + obj.create_prepay_invoice() + def post(self,ids,context={}): obj=self.browse(ids)[0] res=None + if obj.hdcase_reconcile: + obj.clinic_post() if obj.rd_cust: res={} print("RD Customize")