diff --git a/netforce_clinic/layouts/clinic_cust_invoice_form.xml b/netforce_clinic/layouts/clinic_cust_invoice_form.xml index f185557..fcc409b 100644 --- a/netforce_clinic/layouts/clinic_cust_invoice_form.xml +++ b/netforce_clinic/layouts/clinic_cust_invoice_form.xml @@ -3,4 +3,8 @@ + + + + diff --git a/netforce_clinic/layouts/fin_account_settings.xml b/netforce_clinic/layouts/fin_account_settings.xml index e6ae90d..498c3bd 100644 --- a/netforce_clinic/layouts/fin_account_settings.xml +++ b/netforce_clinic/layouts/fin_account_settings.xml @@ -1,5 +1,7 @@ + diff --git a/netforce_clinic/migrations/__init__.py b/netforce_clinic/migrations/__init__.py index 204e2c6..bde4b2c 100644 --- a/netforce_clinic/migrations/__init__.py +++ b/netforce_clinic/migrations/__init__.py @@ -1,7 +1,3 @@ from . import clinic_setting -from . import update_account_tracking -#from . import print_labor_cost -#from . import hdcase -#from . import remove_conv_bal -#from . import import_acc -#from . import update_labor_cost_line +#from . import conv_bal +from . import repost_invoice diff --git a/netforce_clinic/migrations/conv_bal.py b/netforce_clinic/migrations/conv_bal.py new file mode 100644 index 0000000..64cf82d --- /dev/null +++ b/netforce_clinic/migrations/conv_bal.py @@ -0,0 +1,55 @@ +from netforce.model import get_model +from netforce import migration +from netforce.access import set_active_user, set_active_company + +class Migration(migration.Migration): + _name="clinic.conv.bal" + _version="2.10.0" + + def migrate(self): + set_active_user(1) + set_active_company(1) + #for mv in get_model("account.move").search_browse([['number','ilike', 'OPEN']]): + #mv.to_draft() + #mv.delete() + cbv_id=24 + cbv=get_model("conv.bal").browse(cbv_id) + cbv.write({ + 'date_fmt': '%Y-%m-%d', + 'file': 'tb.csv', + }) + print("import acc file (step 1) running ...") + get_model("conv.bal").import_acc_file([cbv.id],context={}) + get_model("conv.bal").import_acc([cbv.id],context={}) + + cbv.write({ + 'date_fmt': '%d/%m/%Y', + 'file': 'ar.csv', + }) + print("import sale file (step 2)running ...") + get_model("conv.bal").import_sale_file([cbv.id],context={}) + get_model("conv.bal").import_sale([cbv.id],context={}) + + print("import purch file (step 3) running ...") + cbv.write({ + 'file': 'ap.csv', + 'date_fmt': '%d/%m/%Y', + }) + get_model("conv.bal").import_purch_file([cbv.id],context={}) + get_model("conv.bal").import_purch([cbv.id],context={}) + + cbv.write({ + 'date_fmt': '%Y-%m-%d', + }) + + print(">> next 3") + print("create_open_entry...") + cbv.create_open_entry() + print("create_sale_invoices...") + cbv.create_sale_invoices() + print("create_purch_invoices...") + cbv.create_purch_invoices() + print("Done!") + return True + +Migration.register() diff --git a/netforce_clinic/migrations/import_acc.py b/netforce_clinic/migrations/import_acc.py deleted file mode 100644 index 95a15a9..0000000 --- a/netforce_clinic/migrations/import_acc.py +++ /dev/null @@ -1,41 +0,0 @@ -from netforce.model import get_model -from netforce import migration -from netforce.access import set_active_user, get_active_user, set_active_company - -class Migration(migration.Migration): - _name="import.acc" - _version="2.11.0" - - def migrate(self): - set_active_company(1) - cbv_id=24 - cbv=get_model("conv.bal").browse(cbv_id) - #cbv.write({ - #'file': 'tb.csv', - #}) - #print("import acc file (step 1) running ...") - #get_model("conv.bal").import_acc([cbv.id],context={}) - - #cbv.write({ - #'file': 'ar.csv', - #}) - #print("import sale file (step 2)running ...") - #get_model("conv.bal").import_sale_file([cbv.id],context={}) - - #print("import purch file (step 3) running ...") - #cbv.write({ - #'file': 'ap.csv', - #}) - #get_model("conv.bal").import_purch([cbv.id],context={}) - #print("create invoice from setep 1 to 3 is running...") - #print("create_open_entry...") - #print('Done!') - #cbv.create_open_entry() - #print("create_sale_invoices...") - #cbv.create_sale_invoices() - #print("create_purch_invoices...") - #cbv.create_purch_invoices() - print("Done!") - return True - -Migration.register() diff --git a/netforce_clinic/migrations/repost_invoice.py b/netforce_clinic/migrations/repost_invoice.py new file mode 100644 index 0000000..401fb53 --- /dev/null +++ b/netforce_clinic/migrations/repost_invoice.py @@ -0,0 +1,39 @@ +from netforce.model import get_model +from netforce import migration +from netforce.access import set_active_user, set_active_company + +class Migration(migration.Migration): + _name="clinic.repos.invoice" + _version="2.10.0" + + def migrate(self): + set_active_user(1) + set_active_company(1) + for hdcase_line in get_model('clinic.hd.case.line').search_browse([['description','=',None]]): + prod=hdcase_line.product_id + if prod: + hdcase_line.write({ + 'description': prod.name, + }) + + for hdcase in get_model('clinic.hd.case').search_browse([]): + for inv in hdcase.invoices: + if inv.state=='waiting_payment': + print('hdcase:repost ---> ', inv.number) + inv.to_draft() + inv.write({ + 'hdcase_reconcile': True, + }) + inv.post() + for shop in get_model('clinic.shop').search_browse([]): + for inv in shop.invoices: + print('shop:repost ---> ', inv.number) + inv.to_draft() + inv.write({ + 'hdcase_reconcile': True, + }) + inv.post() + print("Done!") + return True + +Migration.register() diff --git a/netforce_clinic/models/__init__.py b/netforce_clinic/models/__init__.py index cb004e0..9e9cc5f 100644 --- a/netforce_clinic/models/__init__.py +++ b/netforce_clinic/models/__init__.py @@ -140,3 +140,6 @@ from . import share_location from . import report_cycle_setting 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_invoice.py b/netforce_clinic/models/account_invoice.py index 44d1c39..817f7dd 100644 --- a/netforce_clinic/models/account_invoice.py +++ b/netforce_clinic/models/account_invoice.py @@ -9,6 +9,7 @@ class AccountInvoice(Model): 'department_id': fields.Many2One("clinic.department","Department",search=True), 'patient_partner_id': fields.Many2One("partner","Patient",search=True), 'hdcase_credit': fields.Boolean("HD Case Credit"), + 'hdcase_reconcile': fields.Boolean("HD Case Reconcile"), } def _get_number(self,context={}): @@ -240,15 +241,19 @@ class AccountInvoice(Model): line["credit"]=-amt is_match=False - if settings.acc_prod_match and obj.type=='out': + if obj.hdcase_reconcile and obj.type=='out': print("#POST: clinic customize") cst=get_model('clinic.setting').browse(1) prod_acc=cst.get_product_account move_vals["lines"]=[] for line in group_lines: desc=line['description'] + if not desc: + print("skip no description ", obj.number) + continue ar_debit_id=None #ar_credit_id=None + # search from patient_type for prod_id in get_model('product').search([['name','=',desc]]): for ptype_id in get_model("clinic.patient.type").search([['contact_id','=',partner.id]]): acc=prod_acc(prod_id,ptype_id,'credit') @@ -256,8 +261,16 @@ class AccountInvoice(Model): ar_debit_id=acc.get("ar_debit_id") if ar_debit_id: break + + # search from patient if not ar_debit_id: - raise Exception("Missing AR Debit Account for product %s"%(desc)) + for pt in get_model('clinic.patient').search_browse([['partner_id','=',partner.id]]): + acc=prod_acc(prod_id,pt.type_id.id,'credit') + ar_debit_id=acc.get("ar_debit_id") + if ar_debit_id: + break + if not ar_debit_id: + raise Exception("Missing AR Debit Account for product %s"%(desc), partner.id, partner.name) line_vals={ "description": desc, "account_id": ar_debit_id, 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") diff --git a/netforce_clinic/models/conv_bal.py b/netforce_clinic/models/conv_bal.py new file mode 100644 index 0000000..6023d6e --- /dev/null +++ b/netforce_clinic/models/conv_bal.py @@ -0,0 +1,184 @@ +import csv +import datetime +from io import StringIO + +from netforce.model import Model, fields, get_model +from netforce.utils import get_file_path + +class ConvBal(Model): + _inherit="conv.bal" + + def create_sale_invoices(self,ids,context={}): + obj=self.browse(ids)[0] + desc="Conversion balance %s"%obj.date + for inv in obj.sale_invoices: + vals={ + "type": "out", + "inv_type": inv.amount_due>=0 and "invoice" or "credit", + "partner_id": inv.contact_id.id, + "date": inv.date, + "due_date": inv.due_date, + "number": inv.number, + "ref": inv.ref, + "memo": desc, + "lines": [], + "state": "waiting_payment", + "account_id": inv.account_id.id, + "reconcile_move_line_id": inv.move_line_id.id, + "currency_id": inv.account_id.currency_id.id, + "currency_rate": inv.amount_due/inv.amount_cur if inv.amount_cur else None, + "department_id": inv.department_id.id, + } + line_vals={ + "description": desc, + "amount": abs(inv.amount_cur or inv.amount_due), + "track_id": inv.track_id.id, + } + vals["lines"].append(("create",line_vals)) + res=get_model("account.invoice").search([["number","=",inv.number]]) + if res: + inv2_id=res[0] + inv2=get_model("account.invoice").browse(inv2_id) + #ratchawat + inv2.write({ + 'lines': vals['lines'], + }) + #if abs(inv2.amount_total)-abs(inv.amount_due)>0.001: # XXX + #raise Exception("Failed to update invoice %s: different amount"%inv.number) + if inv2.state=="draft": + raise Exception("Failed to update invoice %s: invalid state"%inv.number) + inv2.write({ + "move_id": obj.move_id.id, + "reconcile_move_line_id": inv.move_line_id.id, + }) + else: + get_model("account.invoice").create(vals) + + def import_sale_file(self,ids,context): + obj=self.browse(ids)[0] + path=get_file_path(obj.file) + data=open(path).read() + rd=csv.reader(StringIO(data)) + headers=next(rd) + headers=[h.strip() for h in headers] + del_ids=get_model("conv.sale.invoice").search([["conv_id","=",obj.id]]) + get_model("conv.sale.invoice").delete(del_ids) + for row in rd: + #print("row",row) + row+="," #XXX append blank column for Amount Cur + line=dict(zip(headers,row)) + #print("line",line) + track_id=None + department_id=None + department_name=line.get("Department") + if department_name: + for dpt in get_model("clinic.department").search_browse([['name','=',department_name]]): + department_id=dpt.id + track_id=dpt.branch_id.track_id.id + if not line.get("Number"): + continue + number=line["Number"].strip() + if not number: + continue + + ##XXX remove invoice + #for inv in get_model('account.invoice').search_browse([['number','=', number]]): + #print('number --> ', number) + #inv.to_draft() + #inv.delete() + #continue + + ref=line["Reference"].strip() + contact_name=line["Contact"].strip() + res=get_model("partner").search([["name","=",contact_name]]) + if not res: + raise Exception("Contact not found: '%s'"%contact_name) + contact_id=res[0] + date=datetime.datetime.strptime(line["Date"].strip(),obj.date_fmt).strftime("%Y-%m-%d") + due_date=datetime.datetime.strptime(line["Due Date"].strip(),obj.date_fmt).strftime("%Y-%m-%d") + amount_due=float(line["Amount Due"].strip().replace(",","") or 0) + acc_code=line["Account"].strip() + amount_cur=float(line["Amount Cur"].strip().replace(",","") or 0) + acc_codes=acc_code.split(",") + amts=[] #XXX + for m in line['Memo'].split(","): + amt=m.split(":")[1] + amt=amt.strip() + if amt: + amt=float(amt) + amts.append(amt) + if len(acc_codes) > 1: + count=0 + print("*"*80) + for acc_code in acc_codes: + acc_code=acc_code.strip() + res=get_model("account.account").search([["code","=",acc_code]]) + if not res: + raise Exception("Account code not found: %s"%acc_code) + acc_id=res[0] + amount_due=amts[count] + vals={ + "conv_id": obj.id, + "number": number, + "ref": ref, + "contact_id": contact_id, + "date": date, + "due_date": due_date, + "amount_due": amount_due, + "account_id": acc_id, + "amount_cur": amount_due, #XXX + 'track_id': track_id, + 'department_id': department_id, + } + get_model("conv.sale.invoice").create(vals) + count+=1 + elif len(amts) >= 1 and len(acc_codes)<=1: + acc_code=acc_codes[0].strip() + res=get_model("account.account").search([["code","=",acc_code]]) + if not res: + raise Exception("Account code not found: %s"%acc_code) + acc_id=res[0] + for amt in amts: + amount_due=amt + vals={ + "conv_id": obj.id, + "number": number, + "ref": ref, + "contact_id": contact_id, + "date": date, + "due_date": due_date, + "amount_due": amount_due, + "account_id": acc_id, + "amount_cur": amount_due, #XXX + 'track_id': track_id, + 'department_id': department_id, + } + get_model("conv.sale.invoice").create(vals) + else: + res=get_model("account.account").search([["code","=",acc_code]]) + if not res: + raise Exception("Account code not found: %s"%acc_code) + acc_id=res[0] + vals={ + "conv_id": obj.id, + "number": number, + "ref": ref, + "contact_id": contact_id, + "date": date, + "due_date": due_date, + "amount_due": amount_due, + "account_id": acc_id, + "amount_cur": amount_cur, + 'track_id': track_id, + 'department_id': department_id, + } + get_model("conv.sale.invoice").create(vals) + return { + "next": { + "name": "conv_bal", + "active_id": obj.id, + "view_xml": "conv_bal2", + } + } + +ConvBal.register() diff --git a/netforce_clinic/models/conv_sale_invoice.py b/netforce_clinic/models/conv_sale_invoice.py new file mode 100644 index 0000000..4ecfb59 --- /dev/null +++ b/netforce_clinic/models/conv_sale_invoice.py @@ -0,0 +1,12 @@ +from netforce.model import Model, fields + +class ConvSaleInvoice(Model): + _inherit="conv.sale.invoice" + _transient=True + _fields={ + "department_id": fields.Many2One("clinic.department","Department"), + "track_id": fields.Many2One("account.track.categ","Track1"), + } + + +ConvSaleInvoice.register() diff --git a/netforce_clinic/models/hd_case.py b/netforce_clinic/models/hd_case.py index 0b3c44d..10cd6f1 100644 --- a/netforce_clinic/models/hd_case.py +++ b/netforce_clinic/models/hd_case.py @@ -681,6 +681,7 @@ class HDCase(Model): "lines": [], "company_id": company_id, 'hdcase_credit': False, + 'hdcase_reconcile': True, } vals["partner_id"]=partner.id vals['lines']=rmb_lines @@ -711,6 +712,7 @@ class HDCase(Model): "company_id": company_id, 'partner_id':partner.id, 'hdcase_credit': True, + 'hdcase_reconcile': True, } vals['lines']=normb_lines if patient_partner: diff --git a/netforce_clinic/models/shop.py b/netforce_clinic/models/shop.py index 028d799..41467df 100644 --- a/netforce_clinic/models/shop.py +++ b/netforce_clinic/models/shop.py @@ -333,6 +333,7 @@ class Shop(Model): "currency_id": currency_id, "company_id": company_id, 'partner_id': partner.id, + 'hdcase_reconcile': True, "lines": [], } track_id=obj.branch_id.track_id.id