From c61517aef4f1ac1d605d7634f129de0203a44d5e Mon Sep 17 00:00:00 2001 From: "watcha.h@almacom.co.th" Date: Thu, 6 Aug 2015 08:37:48 +0700 Subject: [PATCH 1/6] test --- netforce_clinic/migrations/__init__.py | 4 +- netforce_clinic/migrations/import_acc.py | 59 +++++----- netforce_clinic/models/__init__.py | 1 + netforce_clinic/models/conv_bal.py | 135 +++++++++++++++++++++++ 4 files changed, 172 insertions(+), 27 deletions(-) create mode 100644 netforce_clinic/models/conv_bal.py diff --git a/netforce_clinic/migrations/__init__.py b/netforce_clinic/migrations/__init__.py index 204e2c6..1c76385 100644 --- a/netforce_clinic/migrations/__init__.py +++ b/netforce_clinic/migrations/__init__.py @@ -1,7 +1,7 @@ from . import clinic_setting -from . import update_account_tracking +#from . import update_account_tracking #from . import print_labor_cost #from . import hdcase #from . import remove_conv_bal -#from . import import_acc +from . import import_acc #from . import update_labor_cost_line diff --git a/netforce_clinic/migrations/import_acc.py b/netforce_clinic/migrations/import_acc.py index 95a15a9..7aab000 100644 --- a/netforce_clinic/migrations/import_acc.py +++ b/netforce_clinic/migrations/import_acc.py @@ -1,40 +1,49 @@ from netforce.model import get_model from netforce import migration -from netforce.access import set_active_user, get_active_user, set_active_company +from netforce.access import set_active_user, set_active_company class Migration(migration.Migration): _name="import.acc" - _version="2.11.0" + _version="2.10.0" def migrate(self): + set_active_user(1) 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': '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({ - #'file': 'ar.csv', - #}) - #print("import sale file (step 2)running ...") - #get_model("conv.bal").import_sale_file([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={}) + get_model("conv.bal").import_sale([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("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', + }) + + # 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 diff --git a/netforce_clinic/models/__init__.py b/netforce_clinic/models/__init__.py index cb004e0..babbd2b 100644 --- a/netforce_clinic/models/__init__.py +++ b/netforce_clinic/models/__init__.py @@ -140,3 +140,4 @@ from . import share_location from . import report_cycle_setting from . import print_labor_cost from . import print_labor_cost_line +from . import conv_bal diff --git a/netforce_clinic/models/conv_bal.py b/netforce_clinic/models/conv_bal.py new file mode 100644 index 0000000..169ab5e --- /dev/null +++ b/netforce_clinic/models/conv_bal.py @@ -0,0 +1,135 @@ +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] + settings=get_model("settings").browse(1) + 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, + } + line_vals={ + "description": desc, + "amount": abs(inv.amount_cur or inv.amount_due), + } + 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) + if not line.get("Number"): + continue + number=line["Number"].strip() + if not number: + 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(",") + if len(acc_codes) > 1: + count=0 + amts=[m.split(":")[1] for m in line['Memo'].split(",")] + 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_cur, + } + get_model("conv.sale.invoice").create(vals) + count+=1 + 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, + } + get_model("conv.sale.invoice").create(vals) + return { + "next": { + "name": "conv_bal", + "active_id": obj.id, + "view_xml": "conv_bal2", + } + } + +ConvBal.register() From 93ac2c222e4b46aa5541ec3dc4545bc62d5415aa Mon Sep 17 00:00:00 2001 From: "watcha.h" Date: Thu, 6 Aug 2015 11:35:40 +0700 Subject: [PATCH 2/6] xxx --- netforce_clinic/migrations/__init__.py | 2 +- netforce_clinic/migrations/{import_acc.py => conv_bal.py} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename netforce_clinic/migrations/{import_acc.py => conv_bal.py} (98%) diff --git a/netforce_clinic/migrations/__init__.py b/netforce_clinic/migrations/__init__.py index 1c76385..0f34312 100644 --- a/netforce_clinic/migrations/__init__.py +++ b/netforce_clinic/migrations/__init__.py @@ -2,6 +2,6 @@ from . import clinic_setting #from . import update_account_tracking #from . import print_labor_cost #from . import hdcase +from . import conv_bal #from . import remove_conv_bal -from . import import_acc #from . import update_labor_cost_line diff --git a/netforce_clinic/migrations/import_acc.py b/netforce_clinic/migrations/conv_bal.py similarity index 98% rename from netforce_clinic/migrations/import_acc.py rename to netforce_clinic/migrations/conv_bal.py index 7aab000..387a1a3 100644 --- a/netforce_clinic/migrations/import_acc.py +++ b/netforce_clinic/migrations/conv_bal.py @@ -3,7 +3,7 @@ from netforce import migration from netforce.access import set_active_user, set_active_company class Migration(migration.Migration): - _name="import.acc" + _name="clinic.conv.bal" _version="2.10.0" def migrate(self): From 57c5425e30189809eade6d8de3f7a3d735424c95 Mon Sep 17 00:00:00 2001 From: "watcha.h" Date: Fri, 7 Aug 2015 11:15:46 +0700 Subject: [PATCH 3/6] fix --- netforce_clinic/models/conv_bal.py | 50 +++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/netforce_clinic/models/conv_bal.py b/netforce_clinic/models/conv_bal.py index 169ab5e..4bfb59c 100644 --- a/netforce_clinic/models/conv_bal.py +++ b/netforce_clinic/models/conv_bal.py @@ -10,7 +10,6 @@ class ConvBal(Model): def create_sale_invoices(self,ids,context={}): obj=self.browse(ids)[0] - settings=get_model("settings").browse(1) desc="Conversion balance %s"%obj.date for inv in obj.sale_invoices: vals={ @@ -44,12 +43,12 @@ class ConvBal(Model): }) #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, - #}) + 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) @@ -63,10 +62,10 @@ class ConvBal(Model): 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) + #print("row",row) row+="," #XXX append blank column for Amount Cur line=dict(zip(headers,row)) - print("line",line) + #print("line",line) if not line.get("Number"): continue number=line["Number"].strip() @@ -84,9 +83,16 @@ class ConvBal(Model): 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 - amts=[m.split(":")[1] for m in line['Memo'].split(",")] + print("*"*80) for acc_code in acc_codes: acc_code=acc_code.strip() res=get_model("account.account").search([["code","=",acc_code]]) @@ -94,6 +100,7 @@ class ConvBal(Model): raise Exception("Account code not found: %s"%acc_code) acc_id=res[0] amount_due=amts[count] + print(acc_code, acc_id, amount_due) vals={ "conv_id": obj.id, "number": number, @@ -103,10 +110,31 @@ class ConvBal(Model): "due_date": due_date, "amount_due": amount_due, "account_id": acc_id, - "amount_cur": amount_cur, + "amount_cur": amount_due, #XXX } get_model("conv.sale.invoice").create(vals) count+=1 + print("*"*80) + 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 + } + get_model("conv.sale.invoice").create(vals) else: res=get_model("account.account").search([["code","=",acc_code]]) if not res: From e20d25029f89beaf0ee61d177486efcbf09b1afa Mon Sep 17 00:00:00 2001 From: "watcha.h" Date: Mon, 10 Aug 2015 21:38:19 +0700 Subject: [PATCH 4/6] improve conversion balance --- netforce_clinic/migrations/__init__.py | 8 ++---- netforce_clinic/migrations/conv_bal.py | 9 +++++-- netforce_clinic/migrations/repost_invoice.py | 26 ++++++++++++++++++++ netforce_clinic/models/__init__.py | 1 + netforce_clinic/models/account_invoice.py | 12 ++++++++- netforce_clinic/models/conv_bal.py | 25 +++++++++++++++++-- netforce_clinic/models/conv_sale_invoice.py | 12 +++++++++ 7 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 netforce_clinic/migrations/repost_invoice.py create mode 100644 netforce_clinic/models/conv_sale_invoice.py diff --git a/netforce_clinic/migrations/__init__.py b/netforce_clinic/migrations/__init__.py index 0f34312..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 conv_bal -#from . import remove_conv_bal -#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 index 387a1a3..64cf82d 100644 --- a/netforce_clinic/migrations/conv_bal.py +++ b/netforce_clinic/migrations/conv_bal.py @@ -9,16 +9,21 @@ class Migration(migration.Migration): 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 ...") @@ -37,7 +42,7 @@ class Migration(migration.Migration): 'date_fmt': '%Y-%m-%d', }) - # next 3 + print(">> next 3") print("create_open_entry...") cbv.create_open_entry() print("create_sale_invoices...") diff --git a/netforce_clinic/migrations/repost_invoice.py b/netforce_clinic/migrations/repost_invoice.py new file mode 100644 index 0000000..40b2b92 --- /dev/null +++ b/netforce_clinic/migrations/repost_invoice.py @@ -0,0 +1,26 @@ +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 in get_model('clinic.hd.case').search_browse([]): + for inv in hdcase.invoices: + if inv.state=='waiting_payment': + print('repost ---> ', inv.number) + inv.to_draft() + inv.post() + print("Done!") + for shop in get_model('clinic.shop').search_browse([]): + for inv in shop.invoices: + print('repost ---> ', inv.number) + inv.to_draft() + inv.post() + return True + +Migration.register() diff --git a/netforce_clinic/models/__init__.py b/netforce_clinic/models/__init__.py index babbd2b..7c9cbfa 100644 --- a/netforce_clinic/models/__init__.py +++ b/netforce_clinic/models/__init__.py @@ -141,3 +141,4 @@ 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 diff --git a/netforce_clinic/models/account_invoice.py b/netforce_clinic/models/account_invoice.py index 44d1c39..7e40169 100644 --- a/netforce_clinic/models/account_invoice.py +++ b/netforce_clinic/models/account_invoice.py @@ -249,6 +249,7 @@ class AccountInvoice(Model): desc=line['description'] 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 +257,17 @@ 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: + import pdb; pdb.set_trace() + 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/conv_bal.py b/netforce_clinic/models/conv_bal.py index 4bfb59c..6023d6e 100644 --- a/netforce_clinic/models/conv_bal.py +++ b/netforce_clinic/models/conv_bal.py @@ -27,10 +27,12 @@ class ConvBal(Model): "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]]) @@ -66,11 +68,26 @@ class ConvBal(Model): 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]]) @@ -100,7 +117,6 @@ class ConvBal(Model): raise Exception("Account code not found: %s"%acc_code) acc_id=res[0] amount_due=amts[count] - print(acc_code, acc_id, amount_due) vals={ "conv_id": obj.id, "number": number, @@ -111,10 +127,11 @@ class ConvBal(Model): "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 - print("*"*80) elif len(amts) >= 1 and len(acc_codes)<=1: acc_code=acc_codes[0].strip() res=get_model("account.account").search([["code","=",acc_code]]) @@ -133,6 +150,8 @@ class ConvBal(Model): "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: @@ -150,6 +169,8 @@ class ConvBal(Model): "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 { 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() From e26aa9de2c0b3c1cb95b413e3faf9a61a86c3c7d Mon Sep 17 00:00:00 2001 From: "watcha.h" Date: Tue, 11 Aug 2015 11:35:46 +0700 Subject: [PATCH 5/6] migrate post invoice --- .../layouts/clinic_cust_invoice_form.xml | 4 ++++ .../layouts/fin_account_settings.xml | 2 ++ netforce_clinic/migrations/repost_invoice.py | 19 ++++++++++++++++--- netforce_clinic/models/account_invoice.py | 7 +++++-- netforce_clinic/models/hd_case.py | 2 ++ netforce_clinic/models/shop.py | 1 + 6 files changed, 30 insertions(+), 5 deletions(-) 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/repost_invoice.py b/netforce_clinic/migrations/repost_invoice.py index 40b2b92..401fb53 100644 --- a/netforce_clinic/migrations/repost_invoice.py +++ b/netforce_clinic/migrations/repost_invoice.py @@ -9,18 +9,31 @@ class Migration(migration.Migration): 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('repost ---> ', inv.number) + print('hdcase:repost ---> ', inv.number) inv.to_draft() + inv.write({ + 'hdcase_reconcile': True, + }) inv.post() - print("Done!") for shop in get_model('clinic.shop').search_browse([]): for inv in shop.invoices: - print('repost ---> ', inv.number) + 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/account_invoice.py b/netforce_clinic/models/account_invoice.py index 7e40169..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,13 +241,16 @@ 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 @@ -266,7 +270,6 @@ class AccountInvoice(Model): if ar_debit_id: break if not ar_debit_id: - import pdb; pdb.set_trace() raise Exception("Missing AR Debit Account for product %s"%(desc), partner.id, partner.name) line_vals={ "description": desc, diff --git a/netforce_clinic/models/hd_case.py b/netforce_clinic/models/hd_case.py index 787d705..82ecec2 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 @@ -710,6 +711,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 From fdb8620f885f6720e30ed49e57302fb1e41a7d77 Mon Sep 17 00:00:00 2001 From: "watcha.h" Date: Tue, 11 Aug 2015 12:13:04 +0700 Subject: [PATCH 6/6] fix reconcile --- netforce_clinic/models/__init__.py | 1 + netforce_clinic/models/account_move_line.py | 7 + netforce_clinic/models/account_payment.py | 441 ++++++++++++++++++++ 3 files changed, 449 insertions(+) create mode 100644 netforce_clinic/models/account_move_line.py 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")