465 lines
15 KiB
Python
465 lines
15 KiB
Python
import time
|
|
|
|
from netforce.model import Model, fields, get_model
|
|
from netforce.access import get_active_company, get_active_user
|
|
from netforce.utils import get_data_path
|
|
|
|
class CycleItem(Model):
|
|
_name="clinic.cycle.item"
|
|
_string="Cycle Item"
|
|
_name_field="name"
|
|
|
|
def _get_all(self,ids,context={}):
|
|
res={}
|
|
for obj in self.browse(ids):
|
|
name="%s-%s"%(obj.cycle_id.name,obj.date)
|
|
total_case=len(obj.hd_cases)
|
|
total_a,total_b=0,0
|
|
total=0.0
|
|
for line in obj.lines:
|
|
total+=line.amount or 0
|
|
total_a+=line.var_a
|
|
total_b+=line.var_b
|
|
var_ptx=total_case*(obj.var_k or 0)
|
|
var_fml1='PTxK=%sX%s'%(total_a,total_b)
|
|
total_bstr=total_b < 0 and "+%s"%(abs(total_b)) or "-%s"%total_b
|
|
var_x=0.0
|
|
if total_a:
|
|
var_x=eval('(%s%s)/%s'%(var_ptx,total_bstr,total_a))
|
|
res[obj.id]={
|
|
'name': name,
|
|
'var_pt': total_case,
|
|
'var_ptx': total_case*(obj.var_k or 0),
|
|
'var_fml1': '%s'%(var_fml1),
|
|
'var_fml2': '%s=%sX%s'%(var_ptx,total_a,total_b),
|
|
'var_fml3': '(%s%s)/%s'%(var_ptx,total_bstr,total_a),
|
|
'var_x': round(var_x,2),
|
|
'total': total,
|
|
}
|
|
return res
|
|
|
|
_fields={
|
|
'name': fields.Char("Name", function="_get_all",function_multi=True),
|
|
'date': fields.Date("Date",search=True),
|
|
'cycle_id': fields.Many2One("clinic.cycle", "Cycle",search=True),
|
|
'cycle_daily_id': fields.Many2One("clinic.cycle.daily", "Cycle Daily",search=True),
|
|
'lines': fields.One2Many("clinic.cycle.item.line","item_id", "Lines"),
|
|
'visits': fields.One2Many("clinic.visit","cycle_item_id", "Visits"),
|
|
'hd_cases': fields.One2Many("clinic.hd.case","cycle_item_id", "HD Cases"),
|
|
'nurses': fields.One2Many("clinic.cycle.item.nurse",'cycle_item_id','Nurses'),
|
|
'sequence': fields.Char("Sequence"), # for sort item
|
|
'company_id': fields.Many2One("company", "Company"),
|
|
"state": fields.Selection([("draft","Draft"),("done","Done")],"Status",required=True),
|
|
'var_k': fields.Float("K"),
|
|
'var_pt': fields.Integer("PT", function="_get_all",function_multi=True),
|
|
'var_ptx': fields.Char("PT x K:", function="_get_all",function_multi=True),
|
|
'var_fml1': fields.Char("Step 1:",function="_get_all",function_multi=True),
|
|
'var_fml2': fields.Char("Step 2:",function="_get_all",function_multi=True),
|
|
'var_fml3': fields.Char("X:",function="_get_all",function_multi=True),
|
|
'var_x': fields.Char("X:",function="_get_all",function_multi=True),
|
|
'total': fields.Float("Total",function="_get_all",function_multi=True),
|
|
'manual': fields.Boolean("Manual"),
|
|
'user_id': fields.Many2One("base.user","User"),
|
|
}
|
|
|
|
def _get_vark(self,context={}):
|
|
st=get_model("clinic.setting").browse(1)
|
|
return st.var_k or 0
|
|
|
|
_defaults={
|
|
'state': 'draft',
|
|
'company_id': lambda *a: get_active_company(),
|
|
'date': lambda *a: time.strftime("%Y-%m-%d"),
|
|
'user_id': lambda *a: get_active_user(),
|
|
'var_k': _get_vark,
|
|
}
|
|
|
|
_order="date desc"
|
|
_sql_constraints=[
|
|
("cycle_item_uniq","unique (cycle_id,date,company_id)","Cycle item should be unique"),
|
|
]
|
|
|
|
def get_cycle_daily(self,date):
|
|
dom=[]
|
|
print("get ", date)
|
|
dom.append(['date','=',date])
|
|
cd_ids=get_model('clinic.cycle.daily').search(dom)
|
|
cd_id=None
|
|
if cd_ids:
|
|
cd_id=cd_ids[0]
|
|
else:
|
|
cd_id=get_model('clinic.cycle.daily').create({
|
|
'date':date,
|
|
'name':date,
|
|
})
|
|
return cd_id
|
|
|
|
|
|
def create(self, vals,**kw):
|
|
date=vals['date']
|
|
cycle_id=vals['cycle_id']
|
|
vals['cycle_daily_id']=self.get_cycle_daily(date)
|
|
|
|
cycle=get_model("clinic.cycle").browse(cycle_id)
|
|
vals['sequence']='%s-%s'%(date,cycle.sequence) #date-sequence
|
|
obj_id=super().create(vals,**kw)
|
|
return obj_id
|
|
|
|
def write(self,ids,vals,**kw):
|
|
obj=self.browse(ids)[0]
|
|
cycle=obj.cycle_id
|
|
date=vals.get('date') and vals.get('date') or obj.date
|
|
vals['cycle_daily_id']=self.get_cycle_daily(date)
|
|
vals['sequence']='%s-%s'%(obj.date,cycle.sequence) #date-sequence
|
|
super().write(ids,vals,**kw)
|
|
|
|
def validate(self,ids,context={}):
|
|
obj=self.browse(ids)[0]
|
|
levels={}
|
|
for line in obj.lines:
|
|
level=line.level_id
|
|
levels[level.id]={
|
|
'amount': line.amount or 0,
|
|
'qty': line.qty,
|
|
'level_id': level.id,
|
|
}
|
|
|
|
lines=[]
|
|
# cost's nurses
|
|
for nr in obj.nurses:
|
|
nurse=nr.nurse_id
|
|
level=nr.level_id
|
|
vals=levels.get(level.id)
|
|
rate,amt,qty=0.0,0.0,0
|
|
level_id=level.id
|
|
if vals:
|
|
level_id=vals['level_id']
|
|
amt=vals['amount']
|
|
qty=vals['qty']
|
|
if qty:
|
|
rate=amt/qty
|
|
lines.append(('create',{
|
|
'cycle_id': obj.cycle_id.id,
|
|
'staff_id': nurse.id,
|
|
'level_id': level_id,
|
|
'rate': rate,
|
|
'type': 'nurse',
|
|
'qty': 1,
|
|
}))
|
|
|
|
# cost's doctor
|
|
st=get_model('clinic.setting').browse(1)
|
|
cost_per_case=st.cost_per_case or 0
|
|
staff_total={}
|
|
for hd_case in obj.hd_cases:
|
|
staffs=hd_case.staffs
|
|
for ps in staffs:
|
|
staff=ps.staff_id
|
|
if not staff:
|
|
continue
|
|
base=staff.base
|
|
if not base:
|
|
base=cost_per_case
|
|
if not staff_total.get(staff.id):
|
|
staff_total[staff.id]={
|
|
'base': 0,
|
|
'level_id': staff.level_id.id,
|
|
'type': ps.type,
|
|
'qty': 0,
|
|
}
|
|
staff_total[staff.id]['base']=base
|
|
staff_total[staff.id]['qty']+=1
|
|
|
|
for doctor_id, value in staff_total.items():
|
|
base=value['base']
|
|
type=value['type']
|
|
qty=value['qty']
|
|
level_id=value['level_id']
|
|
lines.append(('create',{
|
|
'cycle_id': obj.cycle_id.id,
|
|
'staff_id': doctor_id,
|
|
'level_id': level_id,
|
|
'rate': base,
|
|
'qty': qty,
|
|
'type': type,
|
|
}))
|
|
|
|
cycle_daily=obj.cycle_daily_id
|
|
# clear cost line
|
|
for line in cycle_daily.lines:
|
|
cycle=line.cycle_id
|
|
if obj.cycle_id.id==cycle.id: # only own cycle
|
|
line.delete()
|
|
|
|
# group staff and cycle date
|
|
glines={}
|
|
for line in lines:
|
|
mode,vals=line
|
|
cycle_id=vals['cycle_id']
|
|
staff_id=vals['staff_id']
|
|
#amount=vals['amount'] or 0
|
|
rate=vals['rate'] or 0
|
|
qty=vals['qty'] or 0
|
|
amount=qty*rate
|
|
key=(cycle_id,staff_id)
|
|
if not key in glines.keys():
|
|
glines[key]={
|
|
'amount': amount,
|
|
'type': vals['type'],
|
|
'level_id': vals['level_id'],
|
|
'rate': rate,
|
|
'qty': qty,
|
|
}
|
|
continue
|
|
glines[key]['amount']+=amount
|
|
glines[key]['qty']+=qty
|
|
|
|
lines=[]
|
|
for key,vals in glines.items():
|
|
cycle_id,staff_id=key
|
|
line={
|
|
'cycle_id': cycle_id,
|
|
'staff_id': staff_id,
|
|
'date': obj.date,
|
|
}
|
|
line.update(vals)
|
|
lines.append(('create',line))
|
|
|
|
# call from outside
|
|
if context.get('called'):
|
|
return lines
|
|
|
|
cycle_daily.write({
|
|
'lines': lines,
|
|
})
|
|
|
|
obj.write({
|
|
'state': 'done',
|
|
})
|
|
|
|
return {
|
|
'next': {
|
|
'name': 'clinic_cycle_daily',
|
|
'mode': 'form',
|
|
'active_id': cycle_daily.id,
|
|
},
|
|
'flash': 'Cycle Item has been validated, please see the detail of cost below.',
|
|
}
|
|
|
|
def to_draft(self,ids,context={}):
|
|
obj=self.browse(ids)[0]
|
|
obj.write({
|
|
'state': 'draft',
|
|
})
|
|
|
|
def compute(self,ids,context={}):
|
|
obj=self.browse(ids)[0]
|
|
if not obj.manual:
|
|
for line in obj.lines:
|
|
line.delete()
|
|
|
|
levels={}
|
|
for level_id in get_model('clinic.staff.level').search([['type','=','nurse']]):
|
|
vals={
|
|
level_id: {
|
|
'total': 0,
|
|
}
|
|
}
|
|
levels.update(vals)
|
|
|
|
for ns in obj.nurses:
|
|
level=ns.level_id
|
|
if not level:
|
|
raise Exception("Please specify level for %s"%ns.nurse_id.name)
|
|
levels[level.id]['total']+=1
|
|
|
|
st_levels={}
|
|
st=get_model("clinic.setting").browse(1)
|
|
for line in st.levels:
|
|
level=line.level_id
|
|
st_levels[level.id]={
|
|
'var_a': line.var_a or 0,
|
|
'var_b': line.var_b or 0,
|
|
'op': line.op or "",
|
|
'formular': line.formular or "",
|
|
}
|
|
lines=[]
|
|
total_a,total_b=0,0
|
|
for level_id, value in levels.items():
|
|
qty=value['total']
|
|
st_level=st_levels.get(level_id,None)
|
|
a,b=0,0
|
|
state="fail"
|
|
if st_level:
|
|
state="success"
|
|
var_a=float(st_level['var_a'] or "0")
|
|
var_b=float(st_level['var_b'] or "0")
|
|
fml_org=st_level['formular']
|
|
a=qty*var_a
|
|
a_str='%sX'%(a)
|
|
total_a+=a
|
|
|
|
op=st_level['op']
|
|
sign=op=='-' and -1 or 1
|
|
op_str=sign==1 and op or ""
|
|
|
|
b=(qty*var_b)*sign
|
|
b_str=(str(b) if b else "")
|
|
total_b+=b
|
|
else:
|
|
# XXX prevent to show all level if not configuration from setting
|
|
continue
|
|
|
|
fml=''.join([a_str, op_str, b_str])
|
|
lines.append(('create',{
|
|
'level_id': level_id,
|
|
'var_a': a,
|
|
'var_b': b,
|
|
'qty': qty,
|
|
'formular_org': fml_org,
|
|
'formular': fml if qty else "0",
|
|
'state': state,
|
|
}))
|
|
|
|
if not obj.manual:
|
|
var_pt=len(obj.hd_cases)
|
|
var_ptx=var_pt*(obj.var_k or 0)
|
|
total_bstr=total_b < 0 and "+%s"%(abs(total_b)) or "-%s"%total_b
|
|
var_x=0
|
|
if total_a:
|
|
var_x=eval('(%s%s)/%s'%(var_ptx,total_bstr,total_a))
|
|
for line in lines:
|
|
vals=line[1]
|
|
fml=vals['formular'].replace("X","*%s")
|
|
vals['amount']=0
|
|
if vals['var_a'] or vals['var_b']:
|
|
vals['amount']=eval(fml%var_x)
|
|
else:
|
|
# update qty (a)
|
|
lines=[]
|
|
for line in obj.lines:
|
|
level=line.level_id
|
|
qty=line.qty or 0
|
|
st_level=st_levels.get(level.id,None)
|
|
a,b=0,0
|
|
state="fail"
|
|
if st_level:
|
|
state="success"
|
|
var_a=float(st_level['var_a'] or "0")
|
|
var_b=float(st_level['var_b'] or "0")
|
|
fml_org=st_level['formular']
|
|
a=qty*var_a
|
|
a_str='%sX'%(a)
|
|
|
|
op=st_level['op']
|
|
sign=op=='-' and -1 or 1
|
|
op_str=sign==1 and op or ""
|
|
|
|
b=(qty*var_b)*sign
|
|
b_str=(str(b) if b else "")
|
|
else:
|
|
print("not found in setting ", level.id)
|
|
|
|
fml=''.join([a_str, op_str, b_str])
|
|
lines.append(('write',[line.id],{
|
|
'level_id': level.id,
|
|
'var_a': a,
|
|
'var_b': b,
|
|
'qty': qty,
|
|
'formular': fml if qty else "0",
|
|
'formular_org': fml_org,
|
|
'state': state,
|
|
}))
|
|
|
|
obj.write({
|
|
'lines': lines,
|
|
})
|
|
|
|
if obj.manual:
|
|
for line in obj.lines:
|
|
fml=(line.formular or "").replace("X","*%s")
|
|
if line.var_a or line.var_b:
|
|
amt=eval(fml%obj.var_x)
|
|
else:
|
|
amt=0
|
|
line.write({
|
|
'amount': amt,
|
|
})
|
|
|
|
return {
|
|
'next': {
|
|
'name': 'clinic_cycle_item',
|
|
'mode': 'form',
|
|
'active_id': obj.id,
|
|
},
|
|
'flash':'Compute successfully',
|
|
}
|
|
|
|
def onchange_nurse(self,context={}):
|
|
data=context["data"]
|
|
path=context["path"]
|
|
line=get_data_path(data,path,parent=True)
|
|
nurse_id=line['nurse_id']
|
|
nurse=get_model('clinic.staff').browse(nurse_id)
|
|
line['level_id']=nurse.level_id.id
|
|
return data
|
|
|
|
def view_schedule(self,ids,context={}):
|
|
obj=self.browse(ids)[0]
|
|
date=obj.date
|
|
schd_ids=get_model('clinic.schedule').search([['date','=',date]])
|
|
schedule_id=None
|
|
if schd_ids:
|
|
schedule_id=schd_ids[0]
|
|
return {
|
|
'next': {
|
|
'name': 'clinic_schedule',
|
|
'mode': 'form',
|
|
'active_id': schedule_id,
|
|
}
|
|
}
|
|
|
|
def view_cycle_daily(self,ids,context={}):
|
|
obj=self.browse(ids)[0]
|
|
if not obj.cycle_daily_id:
|
|
raise Exception("Please validate cycle item")
|
|
return {
|
|
'next': {
|
|
'name': 'clinic_cycle_daily',
|
|
'mode': 'form',
|
|
'active_id': obj.cycle_daily_id.id,
|
|
}
|
|
}
|
|
|
|
def load_nurse_from_schedule(self,ids,context={}):
|
|
obj=self.browse(ids)[0]
|
|
#TODO check cycle number & date from schedule
|
|
schedules=get_model("clinic.schedule").search_browse([['date','=',obj.date]])
|
|
nurses=[]
|
|
for schedule in schedules:
|
|
for line in schedule.lines:
|
|
cycle=line.cycle_id
|
|
if obj.cycle_id.id==cycle.id:
|
|
nurse=line.nurse_id
|
|
level=line.level_id
|
|
nurses.append(('create',{
|
|
'nurse_id': nurse.id,
|
|
'level_id': level.id,
|
|
}))
|
|
for nurse in obj.nurses:
|
|
nurse.delete()
|
|
obj.write({
|
|
'nurses': nurses,
|
|
})
|
|
|
|
return {
|
|
'next': {
|
|
'name': 'clinic_cycle_item',
|
|
'mode': 'form',
|
|
'active_id': obj.id,
|
|
},
|
|
'flash': 'Load nurse from schedule to cycle item successfully',
|
|
}
|
|
|
|
CycleItem.register()
|