import time from netforce.model import Model, fields, get_model from netforce.utils import get_file_path from netforce.access import get_active_company, get_active_user from . import utils class AccountInvoice(Model): _inherit="account.invoice" def _get_patient(self,ids,context={}): res={} for obj in self.browse(ids): pt_id=None pt_type_id=None if obj.patient_partner_id: for pt in get_model('clinic.patient').search_browse([['partner_id','=',obj.patient_partner_id.id]]): pt_id=pt.id pt_type_id=pt.type_id.id res[obj.id]={ 'patient_id': pt_id, 'patient_type_id': pt_type_id, } return res _fields={ 'clinic_expense_id': fields.Many2One("clinic.hd.case.expense","Expense"), 'department_id': fields.Many2One("clinic.department","Department",search=True), 'patient_partner_id': fields.Many2One("partner","Partner Patient",search=True), 'patient_id': fields.Many2One("clinic.patient","Patient",function="_get_patient", function_multi=True,store=True,search=True), 'patient_type_id': fields.Many2One("clinic.patient.type","Patient Type",function="_get_patient", function_multi=True,store=True,search=True), 'create_invoice_id': fields.Many2One("create.invoice.payment","Create Invoice Payment"), } def _get_number(self,context={}): defaults=context.get("defaults") if defaults: # XXX type=defaults.get("type") inv_type=defaults.get("inv_type") else: type=context.get("type") inv_type=context.get("inv_type") seq_type=None if type=="out": if inv_type in ("invoice","prepay"): seq_type="cust_invoice" elif inv_type=="credit": seq_type="cust_credit" elif inv_type=="debit": seq_type="cust_debit" elif type=="in": if inv_type in ("invoice","prepay"): seq_type="supp_invoice" elif inv_type=="credit": seq_type="supp_credit" elif inv_type=="debit": seq_type="supp_debit" if not seq_type: return seq_id=get_model("sequence").find_sequence(type=seq_type,context=context) if not seq_id: return None while 1: num=get_model("sequence").get_next_number(seq_id,context=context) res=self.search([["number","=",num]]) if not res: return num get_model("sequence").increment_number(seq_id,context=context) _defaults={ "number": _get_number, } # override this function to push tracking def create_fixed_assets(self,ids,context={}): for obj in self.browse(ids): if obj.fixed_assets: raise Exception("Fixed assets already created for invoice %s"%obj.number) for line in obj.lines: acc=line.account_id if acc.type!="fixed_asset": continue ass_type=acc.fixed_asset_type_id if not ass_type: continue vals={ "name": line.description, "type_id": ass_type.id, "date_purchase": obj.date, "price_purchase": line.amount, # XXX: should be tax-ex "fixed_asset_account_id": acc.id, "dep_rate": ass_type.dep_rate, "dep_method": ass_type.dep_method, "accum_dep_account_id": ass_type.accum_dep_account_id.id, "dep_exp_account_id": ass_type.dep_exp_account_id.id, "invoice_id": obj.id, 'track_id': line.track_id.id, } 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 #XXX ratchawat if not obj.patient_partner_id: 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 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, } #XXX ratchawat: get receive account from invoice directly if obj.patient_partner_id: line_vals['account_id']=obj.account_id.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) def get_invoice_data(self,ids,context={}): settings=get_model('settings').browse(1) pages=[] for obj in self.browse(ids): context['refer_id']=obj.id data=self.get_invoice_page(context=context) limit_item=10 if data['state']=='draft': limit_item-=3 for i in range(len(data['lines']),limit_item): data['lines'].append({ 'no': '', 'product_name': '', 'description': '', 'uom_name': '', 'qty': None, 'price': None, 'amount': None, }) pages.append(data) if pages: pages[-1]["is_last_page"]=True return { "pages": pages, "logo": get_file_path(settings.logo), } def get_invoice_page(self,context={}): if not context.get('refer_id'): return {} inv_id=int(context['refer_id']) inv=self.browse(inv_id) comp_id=get_active_company() comp=get_model('company').browse(comp_id) cust=inv.partner_id cust_tax_no=cust.tax_no or '' cust_name=cust.name or '' cust_addr='' if cust.addresses: cust_addr=cust.addresses[0].address_text if 'your' in cust_addr: cust_addr='' dpt=inv.department_id branch_id=None if dpt: branch_id=dpt.branch_id.id elif inv.patient_partner_id: for pt in get_model('clinic.patient').search_read([['partner_id','=',inv.patient_partner_id.id]],['branch_id']): if pt.get("branch_id"): branch_id=pt['branch_id'][0] context['branch_id']=branch_id st=get_model('settings').browse(1,context=context) cst=get_model('clinic.setting').browse(1) no=1 sub_total=0 amount_total=0 lines=[] for line in inv.lines: amt=line.amount or 0 prod=line.product_id lines.append({ 'no': no, 'product_name': prod.name or '', 'description': line.description or '', 'uom_name': prod.uom_id.name or '', 'qty': line.qty or 0, 'price': line.price or 0, 'amount': amt, }) sub_total+=amt no+=1 amount_total=sub_total is_draft=inv.state=='draft' and True or False is_cheque=False user_id=get_active_user() user=get_model("base.user").browse(user_id) comp_name=comp.name or "" if st.default_address_id.company: comp_name=st.default_address_id.company or "" payment_terms='' currency_code='' due_date=inv.due_date number=inv.number or "" ref=inv.ref or "" payment_terms=inv.payment_terms or "" currency_code=inv.currency_id.code or "" due_date=inv.due_date add=st.default_address_id data={ 'partner_name': cust_name, 'partner_address': cust_addr, 'partner_tax_no': cust_tax_no, 'due_date': due_date, 'currency_code': currency_code, 'payment_terms': payment_terms, 'comp_name': comp_name, 'add_address': add.address or '', 'add_address2': add.address2 or '', 'add_province_name': add.province_id.name or '', 'add_district_name': add.district_id.name or '', 'add_subdistrict_name': add.subdistrict_id.name or '', 'add_city': add.city or '', 'add_postal_code': add.postal_code or '', 'add_phone': add.phone or '', 'add_fax': add.fax or '', 'tax_no': st.tax_no or '', 'number': number, 'ref': ref, 'date': inv.date, 'datenow': inv.date or time.strftime("%d/%m/%Y"), 'dateprint': inv.date or time.strftime("%d/%m/%Y %H:%M:%S"), 'note': inv.note or '', 'lines':lines, 'amount_subtotal': sub_total, 'amount_total': amount_total, 'total_text': utils.num2word(amount_total), 'is_cheque': is_cheque, 'is_draft': is_draft, 'user_name': user.name or "", 'state': inv.state or "", } data['pay_type']='Credit' if st.logo: data['logo']=get_file_path(st.logo) if cst.signature: data['signature']=get_file_path(cst.signature) return data AccountInvoice.register()