invoice matching

conv_bal
watcha.h@almacom.co.th 2014-12-22 00:24:08 +07:00
parent 0b8e46e5b1
commit 75e28cbe9f
10 changed files with 287 additions and 33 deletions

View File

@ -2,7 +2,7 @@
<field name="string">Expenses</field> <field name="string">Expenses</field>
<field name="view_cls">multi_view</field> <field name="view_cls">multi_view</field>
<field name="model">clinic.hd.case.expense</field> <field name="model">clinic.hd.case.expense</field>
<field name="tabs">[["All",[]],["Draft",[["state","=","draft"]]],["Waiting Matching",[["state","=","waiting_matching"]]],["Match",[["state","=","match"]]],["Unmatch",[["state","=","unmatch"]]],["Approved",[["state","=","approved"]]]]</field> <field name="tabs">[["All",[]],["Draft",[["state","=","draft"]]],["Waiting Matching",[["state","=","waiting_matching"]]],["Match",[["state","=","match"]]],["Unmatch",[["state","=","unmatch"]]],["Completed",[["state","=","completed"]]]]</field>
<field name="menu">account_menu</field> <field name="menu">account_menu</field>
<field name="limit">50</field> <field name="limit">50</field>
</action> </action>

View File

@ -5,5 +5,6 @@
<field name="account_fee_id" attrs='{"required":[["type","=","org"]]}'/> <field name="account_fee_id" attrs='{"required":[["type","=","org"]]}'/>
<field name="account_mdc_id" attrs='{"required":[["type","=","org"]]}'/> <field name="account_mdc_id" attrs='{"required":[["type","=","org"]]}'/>
<field name="account_service_id" attrs='{"required":[["type","=","org"]]}'/> <field name="account_service_id" attrs='{"required":[["type","=","org"]]}'/>
<field name="account_payment_id" attrs='{"required":[["type","=","org"]]}'/>
</field> </field>
</inherit> </inherit>

View File

@ -1,4 +1,4 @@
<form model="clinic.hd.case.expense" attrs='{"readonly":[["state","=","approved"]]}'> <form model="clinic.hd.case.expense" attrs='{"readonly":[["state","=","completed"]]}'>
<head> <head>
<field name="state"/> <field name="state"/>
<button string="Options" dropdown="1"> <button string="Options" dropdown="1">
@ -28,7 +28,7 @@
</tab> </tab>
</tabs> </tabs>
<foot> <foot>
<button string="Approve" type="success" icon="ok" method="approve" states="match"/> <button string="Done" type="success" icon="ok" method="complete" states="match"/>
<button string="Match" type="success" icon="ok" method="do_match" states="waiting_matching,unmatch,draft"/> <button string="Match" type="success" icon="ok" method="do_match" states="waiting_matching,unmatch,draft"/>
<button string="Unmatch" type="danger" icon="remove" method="do_unmatch" states="waiting_matching,match,draft"/> <button string="Unmatch" type="danger" icon="remove" method="do_unmatch" states="waiting_matching,match,draft"/>
</foot> </foot>

View File

@ -1,4 +1,4 @@
<list model="clinic.hd.case.expense" colors='{"#cfc":[["state","=","approved"]],"#88BAA4":[["state","=","match"]],"#cccccc":[["state","=","unmatch"]],"#f9e37d":[["state","=","waiting_matching"]],"#bcbbb9":[["state","=","cancelled"]]}'> <list model="clinic.hd.case.expense" colors='{"#cfc":[["state","=","completed"]],"#88BAA4":[["state","=","match"]],"#cccccc":[["state","=","unmatch"]],"#f9e37d":[["state","=","waiting_matching"]],"#bcbbb9":[["state","=","cancelled"]]}'>
<field name="date"/> <field name="date"/>
<field name="hd_case_id"/> <field name="hd_case_id"/>
<field name="patient_id"/> <field name="patient_id"/>

View File

@ -1,9 +1,11 @@
<form model="clinic.report.payment.matching"> <form model="clinic.report.payment.matching">
<field name="file" span="3" required="1"/> <field name="file" span="4" required="1"/>
<field name="type_id" span="3"/> <field name="type_id" onchange="onchange_type" span="3"/>
<!--<field name="date" span="3" mode="month" onchange="onchange_date"/>--> <field name="hcode_id" span="3" attrs='{"invisible":[["show_hcode","=",0]]}'/>
<newline/>
<group span="6" columns="1"> <group span="6" columns="1">
<button string='Match' span="2" type="success" size="small" icon="ok" method="match_invoice"/> <button string='Match' span="2" type="success" size="small" icon="ok" method="match_invoice"/>
<button string='Make A Payment' span="2" type="default" size="small" icon="arrow-right" method="match_invoice"/> <button string='Make A Payment' method="make_payment" span="2" type="default" size="small" icon="arrow-right"/>
</group> </group>
<field name="show_hcode" invisible="1" span="3"/>
</form> </form>

View File

@ -32,12 +32,13 @@ class HDCaseExpense(Model):
'fee': fields.Boolean("Fee"), 'fee': fields.Boolean("Fee"),
'medicine': fields.Boolean("Medicine"), 'medicine': fields.Boolean("Medicine"),
'service': fields.Boolean("Service"), 'service': fields.Boolean("Service"),
'state': fields.Selection([['draft','Draft'],['waiting_matching','Waiting Matching'],['match','Match'],['unmatch','Unmatch'],['approved','Approved']],'State'), 'state': fields.Selection([['draft','Draft'],['waiting_matching','Waiting Matching'],['match','Match'],['unmatch','Unmatch'],['completed','Completed']],'State'),
'note': fields.Text("Note"), 'note': fields.Text("Note"),
'pt_conflict': fields.Boolean("Patient Conclict",function="_get_patient_conflict"), 'pt_conflict': fields.Boolean("Patient Conclict",function="_get_patient_conflict"),
'company_id': fields.Many2One("company","Company"), 'company_id': fields.Many2One("company","Company"),
'match_id': fields.Many2One("clinic.report.payment.matching","Match"), 'match_id': fields.Many2One("clinic.report.payment.matching","Match"),
'invno': fields.Char("Invoice No"), 'invno': fields.Char("Invoice No"),
'ok': fields.Boolean("OK"),
} }
_defaults={ _defaults={
@ -58,16 +59,17 @@ class HDCaseExpense(Model):
}) })
return new_id return new_id
def approve(self,ids,context={}): def complete(self,ids,context={}):
obj=self.browse(ids)[0] obj=self.browse(ids)[0]
obj.write({ obj.write({
'state': 'approved', 'state': 'completed',
}) })
def to_draft(self,ids,context={}): def to_draft(self,ids,context={}):
obj=self.browse(ids)[0] obj=self.browse(ids)[0]
obj.write({ obj.write({
'state': 'draft', 'state': 'draft',
'ok': False,
}) })
def delete(self,ids,context={}): def delete(self,ids,context={}):
@ -81,12 +83,14 @@ class HDCaseExpense(Model):
obj=self.browse(ids)[0] obj=self.browse(ids)[0]
obj.write({ obj.write({
'state': 'match', 'state': 'match',
'ok':True,
}) })
def do_unmatch(self,ids,context={}): def do_unmatch(self,ids,context={}):
obj=self.browse(ids)[0] obj=self.browse(ids)[0]
obj.write({ obj.write({
'state': 'unmatch', 'state': 'unmatch',
'ok': False,
}) })
def cancel(self,ids,context={}): def cancel(self,ids,context={}):

View File

@ -4,9 +4,10 @@ class Partner(Model):
_inherit="partner" _inherit="partner"
_fields={ _fields={
"account_mdc_id": fields.Many2One("account.account","Account Medicine",multi_company=True), "account_mdc_id": fields.Many2One("account.account","Account Receiveable Medicine",multi_company=True),
"account_fee_id": fields.Many2One("account.account","Account Fee",multi_company=True), "account_fee_id": fields.Many2One("account.account","Account Receiveable Fee",multi_company=True),
"account_service_id": fields.Many2One("account.account","Account Service",multi_company=True), "account_service_id": fields.Many2One("account.account","Account Receiveable Service",multi_company=True),
"account_payment_id": fields.Many2One("account.account","Account Payment",multi_company=True),
} }
Partner.register() Partner.register()

View File

@ -3,6 +3,7 @@ from calendar import monthrange
from netforce.model import Model,fields,get_model from netforce.model import Model,fields,get_model
from netforce.access import get_active_company from netforce.access import get_active_company
from netforce.utils import get_file_path
from . import utils from . import utils
STATES={ STATES={
@ -26,6 +27,10 @@ class ReportPaymentMatching(Model):
'patient_id': fields.Many2One("clinic.patient","Patient"), 'patient_id': fields.Many2One("clinic.patient","Patient"),
'file': fields.File("File"), 'file': fields.File("File"),
'type_id': fields.Many2One("clinic.patient.type","Patient Type",required=True), 'type_id': fields.Many2One("clinic.patient.type","Patient Type",required=True),
'hcode_id': fields.Many2One("clinic.hospital","HCode",required=True),
'match_qty': fields.Integer("Match Qty"),
'unmatch_qty': fields.Integer("Unmatch Qty"),
'show_hcode': fields.Integer("Unmatch Qty"),
} }
def _get_date_from(self,context={}): def _get_date_from(self,context={}):
@ -47,6 +52,7 @@ class ReportPaymentMatching(Model):
'date_to': _get_date_to, 'date_to': _get_date_to,
'state': 'match', 'state': 'match',
'type_id': _get_type_id, 'type_id': _get_type_id,
'show_hcode': 0,
} }
def match_invoice(self,ids,context={}): def match_invoice(self,ids,context={}):
@ -56,21 +62,146 @@ class ReportPaymentMatching(Model):
obj=self.browse(ids)[0] obj=self.browse(ids)[0]
if not obj.file: if not obj.file:
raise Exception("File not found!") raise Exception("File not found!")
hcode_impt=obj.hcode_id.code or ""
fname=obj.file
#pks
if obj.type_id.code=='S':
n,sf=fname.split(".")
if sf not in ('xls','xlsx'):
raise Exception("File should be xls or xlsx")
fpath=get_file_path(fname)
lines=utils.read_excel(fpath,show_datetime=True)
elif obj.type_id.code=='U':
fpath=get_file_path(fname)
node='HDBills'
lines=utils.read_xml(fpath,node=node)
hcode_impt=fname.split("_")[0]
print('>> ', hcode_impt)
else:
raise Exception("Script is not support")
if not lines:
raise Exception("No data to match")
dom=[] dom=[]
dom.append(['state','=','waiting_matching']) dom.append(['state','in',['match','waiting_matching']])
dom.append(['ok','=',False])
matches={}
for exp in get_model("clinic.hd.case.expense").search_browse(dom): for exp in get_model("clinic.hd.case.expense").search_browse(dom):
ptype=exp.patient_id.type_id ptype=exp.patient_id.type_id
if ptype.id==obj.type_id.id: if ptype.id==obj.type_id.id:
exp.write({ exp.write({
'match_id': obj.id, 'match_id': obj.id,
'ok': False,
}) })
if not matches.get(exp.date):
patient=exp.patient_id
if obj.type_id.code=='S':
key='%s:%s:%s:%s:%s'%(
patient.hn_num,
exp.date,
exp.fee_amt and exp.fee_amt or 0,
exp.srv_amt and exp.srv_amt or 0,
exp.mdc_amt and exp.mdc_amt or 0,
)
else:
key='%s:%s:%s'%(
patient.hn_num,
exp.date,
exp.fee_amt and exp.fee_amt or 0,
)
matches[key]={
'exp_id': exp.id,
}
else: else:
exp.write({ exp.write({
'match_id': None, 'match_id': None,
'ok': False,
}) })
# TODO
# udpate check state , write ref (invno)
match_qty=0
unmatch_qty=0
if obj.type_id.code=='S':
for line in lines:
hcode=line.get('hcode18')
if not hcode:
hcode='0'
hcode=int(hcode)
hcode=str(hcode)
if hcode_impt==hcode:
lsrv_amt=line.get('epoadm29') or 0
lfee_amt=line.get('amount23') or 0
lmdc_amt=line.get('allow37') or 0
dttran=line.get("dttran")
date=dttran[0:10]
time=dttran[11:]
hn=line.get('hn')
hn=''.join([x for x in hn if x.isdigit()])
key='%s:%s:%s:%s:%s'%(
hn,
date,
lfee_amt,
lsrv_amt,
lmdc_amt,
)
if not time:
print("wrong format")
continue
found=matches.get(key)
if found:
exp_id=found['exp_id']
print("match !! ", key)
exp=get_model("clinic.hd.case.expense").browse(exp_id)
invno=''
if line.get('invno'):
invno='%s'%(int(line['invno']))
exp.write({
'ok': True,
'invno': invno,
'state': 'match',
})
match_qty+=1
else:
unmatch_qty+=1
print("no ", key)
elif obj.type_id.code=='U':
for line in lines:
hn=line.get('hn')
amt=line.get('amount') or 0
dttran=line.get("dttran")
date,time=dttran.split("T")
amt=round(float(amt),1)
key='%s:%s:%s'%(
hn,
date,
amt,
)
found=matches.get(key)
if found:
exp_id=found['exp_id']
exp=get_model("clinic.hd.case.expense").browse(exp_id)
invno=''
if line.get('invno'):
invno='%s'%(int(line['invno']))
exp.write({
'ok': True,
'invno': invno,
'state': 'match',
})
match_qty+=1
else:
unmatch_qty+=1
print("no ", key)
obj.write({
'match_qty': match_qty,
'unmatch_qty': unmatch_qty,
})
return { return {
'next': { 'next': {
'name': 'clinic_report_payment_matching', 'name': 'clinic_report_payment_matching',
@ -87,21 +218,29 @@ class ReportPaymentMatching(Model):
return return
obj=self.browse(ids)[0] obj=self.browse(ids)[0]
dom=[] dom=[]
dom.append(['state','=','waiting_matching']) dom.append(['state','!=','completed'])
dom.append(['match_id','=',obj.id]) dom.append(['match_id','=',obj.id])
match_qty=0
unmatch_qty=0
for exp in get_model("clinic.hd.case.expense").search_browse(dom): for exp in get_model("clinic.hd.case.expense").search_browse(dom):
patient=exp.patient_id patient=exp.patient_id
match='No' match='No'
exp_color="" exp_color=""
if exp.state=='match': match_color=""
if exp.ok:
match='Yes' match='Yes'
exp_color='#489f48'
match_color='white'
match_qty+=1
else: else:
exp_color='#C0C0C0' exp_color='#C0C0C0'
unmatch_qty+=1
lines.append({ lines.append({
'exp_id': exp.id, 'exp_id': exp.id,
'date': exp.date,
'patient_name': patient.name, 'patient_name': patient.name,
'patient_id': patient.id, 'patient_id': patient.id,
'hn': patient.hn, 'hn': patient.hn or "",
'fee_amt': exp.fee_amt or 0, 'fee_amt': exp.fee_amt or 0,
'mdc_amt': exp.mdc_amt or 0, 'mdc_amt': exp.mdc_amt or 0,
'srv_amt': exp.srv_amt or 0, 'srv_amt': exp.srv_amt or 0,
@ -109,10 +248,20 @@ class ReportPaymentMatching(Model):
'match': match, 'match': match,
'invno': exp.invno, 'invno': exp.invno,
'exp_color': exp_color, 'exp_color': exp_color,
'match_color': match_color,
}) })
lines2=[]
no=1
for line in sorted(lines, key=lambda a: a['hn']):
line['no']=no
lines2.append(line)
no+=1
data={ data={
'lines': lines, 'match_qty': match_qty or 0,
'nomatch_qty': unmatch_qty or 0,
'lines': lines2,
} }
return data return data
@ -125,4 +274,75 @@ class ReportPaymentMatching(Model):
data['date_to']="%s-%s-%s"%(year,month,total_day) data['date_to']="%s-%s-%s"%(year,month,total_day)
return data return data
def make_payment(self,ids,context={}):
if not ids:
return
obj=self.browse(ids)[0]
partner=obj.type_id.contact_id
company_id=get_active_company()
vals={
"partner_id": partner.id,
"company_id": company_id,
"type": "in",
"pay_type": "invoice",
'date': time.strftime("%Y-%m-%d"),
"account_id": partner.account_payment_id.id,
'invoice_lines': [],
}
dom=[]
dom.append(['ok','=',True])
dom.append(['state','=','match'])
dom.append(['match_id','=',obj.id])
count=0
for exp in get_model("clinic.hd.case.expense").search_browse(dom):
for inv in exp.invoices:
vals['invoice_lines'].append(('create',{
'invoice_id': inv.id,
'amount': inv.amount_due,
}))
exp.write({
'state': 'completed',
'ok': False,
})
count+=1
obj.write({
'match_qty': 0,
'unmatch_qty': obj.unmatch_qty-obj.match_qty,
})
if not count:
return
payment_id=get_model("account.payment").create(vals,context={"type":"in"})
return {
'next': {
'name': 'payment',
'mode': 'form',
'active_id': payment_id,
},
'flash': 'Create payment for match items',
}
def onchange_type(self,context={}):
data=context['data']
type_id=data['type_id']
ptype=get_model("clinic.patient.type").browse(type_id)
if ptype.code=='U':
data['show_hcode']=0
else:
data['show_hcode']=1
for exp in get_model("clinic.hd.case.expense").search_browse([]):
if ptype.id==exp.patient_id.type_id.id and exp.ok:
exp.write({
'ok': False,
})
print("Done!")
return data
ReportPaymentMatching.register() ReportPaymentMatching.register()

View File

@ -73,7 +73,16 @@ class ClinicSetting(Model):
return data return data
def update_date(self,ids,context={}): def update_date(self,ids,context={}):
pass for inv in get_model("account.invoice").search_browse([]):
amount_total=0
for line in inv.lines:
amount_total+=line.amount
inv.write({
'amount_total': amount_total,
'amount_due': amount_total,
})
print("Done!")
#for hd_case in get_model("clinic.hd.case").search_browse([]): #for hd_case in get_model("clinic.hd.case").search_browse([]):
#if hd_case.state!='waiting_treatment': #if hd_case.state!='waiting_treatment':
#continue #continue

View File

@ -1,29 +1,46 @@
<center> <center>
<h4>Payment Matching</h4> <h3>Invoice Matching</h3>
<p>
<span style="height: 20px; width: 100px; background-color: #419641; padding-right: 20px;">
<b>
Match: {{match_qty}}
</b>
</span>
<span style="height: 20px; width: 100px; background-color: red; padding-left: 20px;">
<b>
Not Match: {{nomatch_qty}}
</b>
</span>
</p>
</center> </center>
{{#if lines}} {{#if lines}}
<table class="table table-condensed table-striped"> <table class="table table-condensed table-striped">
<thead> <thead>
<th>#</th>
<th>HN</th> <th>HN</th>
<th>Patient</th> <th>Patient</th>
<th>Date</th>
<th>Invoice No</th> <th>Invoice No</th>
<th>Fee</th> <th>Fee</th>
<th>Medicine</th> <th>Medicine</th>
<th>Service</th> <th>Service</th>
<th>Match</th> <th>Match</th>
<th></th>
</thead> </thead>
<tbody> <tbody>
{{#each lines}} {{#each lines}}
<tr> <tr>
<td style="background-color:{{exp_color}}"><a href="/ui#name=clinic_patient&active_id={{patient_id}}&mode=form">{{hn}}</a></td> <td style="background-color:{{expx_color}}">{{no}}</td>
<td style="background-color:{{exp_color}}"><a href="/ui#name=clinic_patient&active_id={{patient_id}}&mode=form">{{patient_name}}</a></td> <td style="background-color:{{expx_color}}"><a href="/ui#name=clinic_patient&active_id={{patient_id}}&mode=form">{{hn}}</a></td>
<td style="background-color:{{exp_color}}">{{invno}}</td> <td style="background-color:{{expx_color}}"><a href="/ui#name=clinic_patient&active_id={{patient_id}}&mode=form">{{patient_name}}</a></td>
<td style="background-color:{{exp_color}}">{{fee_amt}}</td> <td style="background-color:{{expx_color}}">{{date}}</td>
<td style="background-color:{{exp_color}}">{{mdc_amt}}</td> <td style="background-color:{{expx_color}}">{{invno}}</td>
<td style="background-color:{{exp_color}}">{{srv_amt}}</td> <td style="background-color:{{expx_color}}">{{fee_amt}}</td>
<td style="background-color:{{exp_color}}">{{match}}</td> <td style="background-color:{{expx_color}}">{{mdc_amt}}</td>
<td style="background-color:{{exp_color}}"><a href="/ui#name=clinic_hd_case_expense&active_id={{exp_id}}&mode=form">Edit</a></td> <td style="background-color:{{expx_color}}">{{srv_amt}}</td>
<td style="background-color:{{exp_color}}">
<a style="" href="/ui#name=clinic_hd_case_expense&active_id={{exp_id}}&mode=form">{{match}}</a>
</td>
</tr> </tr>
{{/each}} {{/each}}
</tbody> </tbody>