diff --git a/netforce_clinic/layouts/clinic_account_setting.xml b/netforce_clinic/layouts/clinic_account_setting.xml index b3cc3f5..d0ff53a 100644 --- a/netforce_clinic/layouts/clinic_account_setting.xml +++ b/netforce_clinic/layouts/clinic_account_setting.xml @@ -33,16 +33,6 @@ - - - - - - - - - - @@ -57,15 +47,6 @@ - - - - - - - - - diff --git a/netforce_clinic/layouts/fin_account_settings.xml b/netforce_clinic/layouts/fin_account_settings.xml new file mode 100644 index 0000000..e6ae90d --- /dev/null +++ b/netforce_clinic/layouts/fin_account_settings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/netforce_clinic/models/account_invoice.py b/netforce_clinic/models/account_invoice.py index 77bb01a..44d1c39 100644 --- a/netforce_clinic/models/account_invoice.py +++ b/netforce_clinic/models/account_invoice.py @@ -1,3 +1,5 @@ +import time + from netforce.model import Model, fields, get_model class AccountInvoice(Model): @@ -75,4 +77,237 @@ class AccountInvoice(Model): } get_model("account.fixed.asset").create(vals) + def post(self,ids,context={}): + t0=time.time() + settings=get_model("settings").browse(1) + for obj in self.browse(ids): + obj.check_related() + if abs(obj.amount_total)<0.001: + raise Exception("Invoice total is zero") + partner=obj.partner_id + if obj.type=="out": + 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=="in": + account_id=partner.account_payable_id.id or settings.account_payable_id.id + if not account_id: + raise Exception("Account payable not found") + sign=obj.type=="out" and 1 or -1 + if obj.inv_type=="credit": + sign*=-1 + obj.write({"account_id": account_id}) + if obj.type=="out": + desc="Sale; "+partner.name + elif obj.type=="in": + desc="Purchase; "+partner.name + if obj.type=="out": + journal_id=settings.sale_journal_id.id + if not journal_id: + raise Exception("Sales journal not found") + elif obj.type=="in": + journal_id=settings.purchase_journal_id.id + if not journal_id: + raise Exception("Purchases journal not found") + 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 + obj.write({"currency_rate":currency_rate}) + move_vals={ + "journal_id": journal_id, + "number": obj.number, + "date": obj.date, + "ref": obj.ref, + "narration": desc, + "related_id": "account.invoice,%s"%obj.id, + "company_id": obj.company_id.id, + } + lines=[] + taxes={} + tax_nos=[] + t01=time.time() + total_amt=0.0 + total_base=0.0 + total_tax=0.0 + for line in obj.lines: + cur_amt=get_model("currency").convert(line.amount,obj.currency_id.id,settings.currency_id.id,rate=currency_rate) + total_amt+=cur_amt + tax_id=line.tax_id + if tax_id 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="invoice") + for comp_id,tax_amt in tax_comps.items(): + tax_vals=taxes.setdefault(comp_id,{"tax_amt":0,"base_amt":0}) + tax_vals["tax_amt"]+=tax_amt + tax_vals["base_amt"]+=base_amt + total_tax+=tax_amt + else: + base_amt=cur_amt + total_base+=base_amt + acc_id=line.account_id.id + if not acc_id: + raise Exception("Missing line account for invoice line '%s'"%line.description) + amt=base_amt*sign + line_vals={ + "description": line.description, + "account_id": acc_id, + "credit": amt>0 and amt or 0, + "debit": amt<0 and -amt or 0, + "track_id": line.track_id.id, + "track2_id": line.track2_id.id, + "partner_id": partner.id, + } + lines.append(line_vals) + for comp_id,tax_vals in 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) + amt=tax_vals["tax_amt"]*sign + line_vals={ + "description": desc, + "account_id": acc_id, + "credit": amt>0 and amt or 0, + "debit": amt<0 and -amt or 0, + "tax_comp_id": comp_id, + "tax_base": tax_vals["base_amt"], + "partner_id": partner.id, + "invoice_id": obj.id, + } + if comp.type in ("vat","vat_exempt"): + if obj.type=="out": + if obj.tax_no: + tax_no=obj.tax_no + else: + tax_no=self.gen_tax_no(exclude=tax_nos,context={"date":obj.date}) + tax_nos.append(tax_no) + obj.write({"tax_no":tax_no}) + line_vals["tax_no"]=tax_no + elif obj.type=="in": + line_vals["tax_no"]=obj.tax_no + lines.append(line_vals) + if obj.tax_type=="tax_in": + rounding=total_amt-(total_base+total_tax) + if abs(rounding)>0.00499: # XXX + amt=rounding*sign + if not settings.rounding_account_id.id: + raise Exception("Missing rounding account in financial settings") + line_vals={ + "description": desc, + "account_id": settings.rounding_account_id.id, + "credit": amt>0 and amt or 0, + "debit": amt<0 and -amt or 0, + "partner_id": partner.id, + "invoice_id": obj.id, + } + lines.append(line_vals) + t02=time.time() + dt01=(t02-t01)*1000 + print("post dt01",dt01) + groups={} + keys=["description","account_id","track_id","tax_comp_id","partner_id","invoice_id","reconcile_id"] + for line in lines: + key_val=tuple(line.get(k) for k in keys) + if key_val in groups: + group=groups[key_val] + group["debit"]+=line["debit"] + group["credit"]+=line["credit"] + if line.get("tax_base"): + if "tax_base" not in group: + group["tax_base"]=0 + group["tax_base"]+=line["tax_base"] + else: + groups[key_val]=line.copy() + group_lines=sorted(groups.values(),key=lambda l: (l["debit"],l["credit"])) + for line in group_lines: + amt=line["debit"]-line["credit"] + amt=get_model("currency").round(settings.currency_id.id,amt) + if amt>=0: + line["debit"]=amt + line["credit"]=0 + else: + line["debit"]=0 + line["credit"]=-amt + + is_match=False + if settings.acc_prod_match 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'] + ar_debit_id=None + #ar_credit_id=None + 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') + #ar_credit_id=acc.get("ar_credit_id") + 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)) + line_vals={ + "description": desc, + "account_id": ar_debit_id, + "debit": line['credit'], + "credit": 0, + "due_date": obj.due_date, + "partner_id": partner.id, + 'track_id': line['track_id'], + } + move_vals["lines"]+=[("create",line_vals)] + move_vals["lines"]+=[("create",vals) for vals in group_lines] + is_match=True + if not is_match: + print("#POST: standard account") + amt=0 + for line in group_lines: + amt-=line["debit"]-line["credit"] + line_vals={ + "description": desc, + "account_id": account_id, + "debit": amt>0 and amt or 0, + "credit": amt<0 and -amt or 0, + "due_date": obj.due_date, + "partner_id": partner.id, + } + acc=get_model("account.account").browse(account_id) + if acc.currency_id.id!=settings.currency_id.id: + if acc.currency_id.id!=obj.currency_id.id: + raise Exception("Invalid account currency for this invoice: %s"%acc.code) + line_vals["amount_cur"]=obj.amount_total*sign + move_vals["lines"]=[("create",line_vals)] + move_vals["lines"]+=[("create",vals) for vals in group_lines] + + t03=time.time() + dt02=(t03-t02)*1000 + print("post dt02",dt02) + move_id=get_model("account.move").create(move_vals) + t04=time.time() + dt03=(t04-t03)*1000 + print("post dt03",dt03) + get_model("account.move").post([move_id]) + t05=time.time() + dt04=(t05-t04)*1000 + print("post dt04",dt04) + obj.write({"move_id":move_id,"state":"waiting_payment"}) + t06=time.time() + dt05=(t06-t05)*1000 + print("post dt05",dt05) + t1=time.time() + dt=(t1-t0)*1000 + print("invoice.post <<< %d ms"%dt) + AccountInvoice.register() diff --git a/netforce_clinic/models/fin_setting.py b/netforce_clinic/models/fin_setting.py index dba9c55..3c9e9a4 100644 --- a/netforce_clinic/models/fin_setting.py +++ b/netforce_clinic/models/fin_setting.py @@ -8,6 +8,7 @@ class Settings(Model): "income_account_id": fields.Many2One("account.account","Income Account"), "ap_nurse_id": fields.Many2One("account.account","Account Payment Nurse"), "ap_doctor_id": fields.Many2One("account.account","Account Payment Doctor"), + 'acc_prod_match': fields.Boolean("Account Product Matching"), } def get_default_address(self,ids,context={}): diff --git a/netforce_clinic/models/hd_case.py b/netforce_clinic/models/hd_case.py index 82f00a1..787d705 100644 --- a/netforce_clinic/models/hd_case.py +++ b/netforce_clinic/models/hd_case.py @@ -104,6 +104,7 @@ class HDCase(Model): dlz_name=','.join([dlz for dlz in dlz_name]) epo_names=[] mdc_names=[] + iron_names=[] fee=0 lab=0 misc=0 @@ -113,6 +114,7 @@ class HDCase(Model): for line in obj.lines: amt=line.amount or 0 prod=line.product_id + prod_name=prod.description or prod.name or "" categ=line.product_categ_id if categ and prod: sign=1 @@ -120,24 +122,17 @@ class HDCase(Model): sign=-1 if categ.parent_id: if categ.parent_id.code=='MDC': - name=prod.description or prod.name or "" - ##name=name.split("-") - #name=name.replace("-SSO","") - #name=name.title() - #name=name[0].title() if reimbursable_ctx: if reimbursable_ctx==line.reimbursable: mdc+=amt - mdc_names.append(name or "") + mdc_names.append(prod_name or "") else: mdc+=amt mdc_names.append(name or "") if categ.code=='EPO': - name=prod.description or prod.name or "" - #name=name.split("-") - #if name: - #name=name[0].title() - epo_names.append(name.title()) + epo_names.append(prod_name.title()) + elif categ.code=='IVR': + iron_names.append(prod_name.title()) elif categ.code=='FEE': fee+=amt*sign elif categ.code=='DLZ': @@ -158,12 +153,13 @@ class HDCase(Model): misc+=amt res[obj.id]={ 'epo': ','.join([n for n in epo_names]), + 'mdc_name': ','.join([n for n in mdc_names]), + 'iron_name': ','.join([n for n in iron_names]), 'fee': fee, 'lab': lab, 'misc': misc, 'mdc': mdc, 'srv': srv, - 'mdc_name': ','.join([n for n in mdc_names]), 'dlz_id': dlz_id, 'dlz_price': dlz_price, 'dlz_name': dlz_name, @@ -558,7 +554,6 @@ class HDCase(Model): } payment_id=get_model("account.payment").create(vals,context=context) obj.write({ - #'state': 'paid', 'payment_lines': [('create',{ 'payment_id': payment_id, 'amount': pay_amount, diff --git a/netforce_clinic/models/hd_case_payment.py b/netforce_clinic/models/hd_case_payment.py index 402a7c3..d226259 100644 --- a/netforce_clinic/models/hd_case_payment.py +++ b/netforce_clinic/models/hd_case_payment.py @@ -45,7 +45,7 @@ class HDCasePayment(Model): hd_case.post_invoices() if obj.pay_amount: hd_case.make_payment(context=context) - hd_case.do_expense() + #hd_case.do_expense() inv_remain_amount=0 for inv in hd_case.invoices: @@ -82,7 +82,7 @@ class HDCasePayment(Model): hd_case.make_invoices(context=context) hd_case.post_invoices() hd_case.create_cycle_item() - hd_case.do_expense() + #hd_case.do_expense() hd_case.write({ 'state': 'waiting_payment', 'req_fee': 0, # force to hide button pay! diff --git a/netforce_clinic/models/report_cycle_item.py b/netforce_clinic/models/report_cycle_item.py index 98dc708..d66dfc2 100644 --- a/netforce_clinic/models/report_cycle_item.py +++ b/netforce_clinic/models/report_cycle_item.py @@ -247,12 +247,14 @@ class ReportCycleItem(Model): epo_items=[{'name': k, 'qty': v} for k,v in epos.items() if k ] nlines.append({ 'sub': 'show', + 'is_sub': True, 'sub_txt': 'รวม', 'row_color': '#dfdfdf', 'no': count, 'fee': sub_fee, 'mdc': sub_mdc, 'epo_items': epo_items, + 'epo_txt': ', '.join(['%s = %s'%(k,v) for k,v in epos.items() if k]) }) for epo_item in epo_items: if not total_epos.get(epo_item['name']): @@ -285,6 +287,9 @@ class ReportCycleItem(Model): }) index+=1 vscl_txt=' '.join(['%s = %s'%(x['description'],x['qty']) for x in vscl_lines]) + total_epo_txt='' + for t_epo in total_epo_items: + total_epo_txt+='%s = %s '%(t_epo['name'], t_epo['qty']) data={ 'company_name': company_name or "", 'lines': nlines, @@ -301,6 +306,7 @@ class ReportCycleItem(Model): 'total_pt': total_pt, 'total_epo': sum([x['qty'] for x in total_epo_items]), 'total_epo_items': total_epo_items, + 'total_epo_txt': total_epo_txt, } return data diff --git a/netforce_clinic/reports/report_cycle_item.xlsx b/netforce_clinic/reports/report_cycle_item.xlsx index 9b6dd22..d867e67 100644 Binary files a/netforce_clinic/reports/report_cycle_item.xlsx and b/netforce_clinic/reports/report_cycle_item.xlsx differ diff --git a/netforce_clinic/templates/report_cycle_item.hbs b/netforce_clinic/templates/report_cycle_item.hbs index 26d0de7..7e902e5 100644 --- a/netforce_clinic/templates/report_cycle_item.hbs +++ b/netforce_clinic/templates/report_cycle_item.hbs @@ -13,7 +13,8 @@ ชื่อ-สกุล แพทย์ สิทธ์ - ยาฉีด + EPO + Iron HCT DZ N/U @@ -38,18 +39,19 @@ {{dname}} {{tname}} {{mdc_name}} + {{iron_name}} {{hct}} {{dlz_name}} {{dlz_use}} {{nfirst_name}} {{else}} {{#ifeq sub "show"}} - ผู้ป่วย | ยาฉีด + รวม {{no}} - + {{#each epo_items}} - {{name}} : {{qty}}     + {{name}} = {{qty}}     {{/each}} {{else}} @@ -61,6 +63,7 @@ {{dname}} {{tname}} {{mdc_name}} + {{iron_name}} {{hct}} {{dlz_name}} {{dlz_use}} @@ -73,7 +76,7 @@ ผู้ป่วยทั้งหมด {{total_pt}} - +

{{#each ptype_lines}} {{name}} : {{qty}}     @@ -87,10 +90,10 @@ - ยาฉีดทั้งหมด + ยาทั้งหมด {{total_epo}} - +

{{#each total_epo_items}} {{name}} = {{qty}}