Home | Trees | Indices | Help |
|
---|
|
1 # -*- coding: utf8 -*- 2 """Billing code. 3 4 Copyright: authors 5 """ 6 #============================================================ 7 __author__ = "Nico Latzer <nl@mnet-online.de>, Karsten Hilbert <Karsten.Hilbert@gmx.net>" 8 __license__ = 'GPL v2 or later (details at http://www.gnu.org)' 9 10 import sys 11 import logging 12 13 14 if __name__ == '__main__': 15 sys.path.insert(0, '../../') 16 from Gnumed.pycommon import gmPG2 17 from Gnumed.pycommon import gmBusinessDBObject 18 from Gnumed.pycommon import gmTools 19 from Gnumed.pycommon import gmDateTime 20 from Gnumed.business import gmDemographicRecord 21 from Gnumed.business import gmDocuments 22 23 _log = logging.getLogger('gm.bill') 24 25 INVOICE_DOCUMENT_TYPE = u'invoice' 26 #============================================================ 27 # billables 28 #------------------------------------------------------------ 29 _SQL_get_billable_fields = u"SELECT * FROM ref.v_billables WHERE %s" 3032 """Items which can be billed to patients.""" 33 34 _cmd_fetch_payload = _SQL_get_billable_fields % u"""pk_billable = %s""" 35 _cmds_store_payload = [ 36 u"""UPDATE ref.billable SET 37 code = %(billable_code)s, 38 term = %(billable_description)s, 39 amount = %(raw_amount)s, 40 currency = %(currency)s, 41 vat_multiplier = %(vat_multiplier)s 42 WHERE 43 pk = %(pk_billabs)s 44 AND 45 xmin = %(xmin_billable)s 46 RETURNING 47 xmin AS xmin_billable 48 """] 49 50 _updatable_fields = [ 51 'billable_description', 52 'raw_amount', 53 'vat_multiplier', 54 ] 55 #--------------------------------------------------------91 #------------------------------------------------------------57 txt = u'%s [#%s]\n\n' % ( 58 gmTools.bool2subst ( 59 self._payload[self._idx['active']], 60 _('Active billable item'), 61 _('Inactive billable item') 62 ), 63 self._payload[self._idx['pk_billable']] 64 ) 65 txt += u' %s: %s\n' % ( 66 self._payload[self._idx['billable_code']], 67 self._payload[self._idx['billable_description']] 68 ) 69 txt += _(' %(curr)s%(raw_val)s + %(perc_vat)s%% VAT = %(curr)s%(val_w_vat)s\n') % { 70 'curr': self._payload[self._idx['currency']], 71 'raw_val': self._payload[self._idx['raw_amount']], 72 'perc_vat': self._payload[self._idx['vat_multiplier']] * 100, 73 'val_w_vat': self._payload[self._idx['amount_with_vat']] 74 } 75 txt += u' %s %s%s (%s)' % ( 76 self._payload[self._idx['catalog_short']], 77 self._payload[self._idx['catalog_version']], 78 gmTools.coalesce(self._payload[self._idx['catalog_language']], u'', ' - %s'), 79 self._payload[self._idx['catalog_long']] 80 ) 81 txt += gmTools.coalesce(self._payload[self._idx['comment']], u'', u'\n %s') 82 83 return txt84 #--------------------------------------------------------86 cmd = u'SELECT EXISTS(SELECT 1 FROM bill.bill_item WHERE fk_billable = %(pk)s LIMIT 1)' 87 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self._payload[self._idx['pk_billable']]}}]) 88 return rows[0][0]89 90 is_in_use = property(_get_is_in_use, lambda x:x)93 94 if order_by is None: 95 order_by = u' ORDER BY catalog_long, catalog_version, billable_code' 96 else: 97 order_by = u' ORDER BY %s' % order_by 98 99 if active_only: 100 where = u'active IS true' 101 else: 102 where = u'true' 103 104 cmd = (_SQL_get_billable_fields % where) + order_by 105 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 106 return [ cBillable(row = {'data': r, 'idx': idx, 'pk_field': 'pk_billable'}) for r in rows ]107 #------------------------------------------------------------109 cmd = u""" 110 DELETE FROM ref.billable 111 WHERE 112 pk = %(pk)s 113 AND 114 NOT EXISTS ( 115 SELECT 1 FROM bill.bill_item WHERE fk_billable = %(pk)s 116 ) 117 """ 118 args = {'pk': pk_billable} 119 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])120 #============================================================ 121 # bill items 122 #------------------------------------------------------------ 123 _SQL_fetch_bill_item_fields = u"SELECT * FROM bill.v_bill_items WHERE %s" 124126 127 _cmd_fetch_payload = _SQL_fetch_bill_item_fields % u"pk_bill_item = %s" 128 _cmds_store_payload = [ 129 u"""UPDATE bill.bill_item SET 130 fk_provider = %(pk_provider)s, 131 fk_encounter = %(pk_encounter_to_bill)s, 132 date_to_bill = %(raw_date_to_bill)s, 133 description = gm.nullify_empty_string(%(item_detail)s), 134 net_amount_per_unit = %(net_amount_per_unit)s, 135 currency = gm.nullify_empty_string(%(currency)s), 136 fk_bill = %(pk_bill)s, 137 unit_count = %(unit_count)s, 138 amount_multiplier = %(amount_multiplier)s 139 WHERE 140 pk = %(pk_bill_item)s 141 AND 142 xmin = %(xmin_bill_item)s 143 RETURNING 144 xmin AS xmin_bill_item 145 """] 146 147 _updatable_fields = [ 148 'pk_provider', 149 'pk_encounter_to_bill', 150 'raw_date_to_bill', 151 'item_detail', 152 'net_amount_per_unit', 153 'currency', 154 'pk_bill', 155 'unit_count', 156 'amount_multiplier' 157 ] 158 #--------------------------------------------------------233 #------------------------------------------------------------160 txt = u'%s (%s %s%s) [#%s]\n' % ( 161 gmTools.bool2subst( 162 self._payload[self._idx['pk_bill']] is None, 163 _('Open item'), 164 _('Billed item'), 165 ), 166 self._payload[self._idx['catalog_short']], 167 self._payload[self._idx['catalog_version']], 168 gmTools.coalesce(self._payload[self._idx['catalog_language']], u'', ' - %s'), 169 self._payload[self._idx['pk_bill_item']] 170 ) 171 txt += u' %s: %s\n' % ( 172 self._payload[self._idx['billable_code']], 173 self._payload[self._idx['billable_description']] 174 ) 175 txt += gmTools.coalesce ( 176 self._payload[self._idx['billable_comment']], 177 u'', 178 u' (%s)\n', 179 ) 180 txt += gmTools.coalesce ( 181 self._payload[self._idx['item_detail']], 182 u'', 183 _(' Details: %s\n'), 184 ) 185 186 txt += u'\n' 187 txt += _(' %s of units: %s\n') % ( 188 gmTools.u_numero, 189 self._payload[self._idx['unit_count']] 190 ) 191 txt += _(' Amount per unit: %(curr)s%(val_p_unit)s (%(cat_curr)s%(cat_val)s per catalog)\n') % { 192 'curr': self._payload[self._idx['currency']], 193 'val_p_unit': self._payload[self._idx['net_amount_per_unit']], 194 'cat_curr': self._payload[self._idx['billable_currency']], 195 'cat_val': self._payload[self._idx['billable_amount']] 196 } 197 txt += _(' Amount multiplier: %s\n') % self._payload[self._idx['amount_multiplier']] 198 txt += _(' VAT would be: %(perc_vat)s%% %(equals)s %(curr)s%(vat)s\n') % { 199 'perc_vat': self._payload[self._idx['vat_multiplier']] * 100, 200 'equals': gmTools.u_corresponds_to, 201 'curr': self._payload[self._idx['currency']], 202 'vat': self._payload[self._idx['vat']] 203 } 204 205 txt += u'\n' 206 txt += _(' Charge date: %s') % gmDateTime.pydt_strftime ( 207 self._payload[self._idx['date_to_bill']], 208 '%Y %b %d', 209 accuracy = gmDateTime.acc_days 210 ) 211 bill = self.bill 212 if bill is not None: 213 txt += _('\n On bill: %s') % bill['invoice_id'] 214 215 return txt216 #--------------------------------------------------------218 return cBillable(aPK_obj = self._payload[self._idx['pk_billable']])219 220 billable = property(_get_billable, lambda x:x) 221 #--------------------------------------------------------223 if self._payload[self._idx['pk_bill']] is None: 224 return None 225 return cBill(aPK_obj = self._payload[self._idx['pk_bill']])226 227 bill = property(_get_bill, lambda x:x) 228 #-------------------------------------------------------- 231 232 is_in_use = property(_get_is_in_use, lambda x:x)235 if non_invoiced_only: 236 cmd = _SQL_fetch_bill_item_fields % u"pk_patient = %(pat)s AND pk_bill IS NULL" 237 else: 238 cmd = _SQL_fetch_bill_item_fields % u"pk_patient = %(pat)s" 239 args = {'pat': pk_patient} 240 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 241 return [ cBillItem(row = {'data': r, 'idx': idx, 'pk_field': 'pk_bill_item'}) for r in rows ]242 #------------------------------------------------------------244 245 billable = cBillable(aPK_obj = pk_billable) 246 cmd = u""" 247 INSERT INTO bill.bill_item ( 248 fk_provider, 249 fk_encounter, 250 net_amount_per_unit, 251 currency, 252 fk_billable 253 ) VALUES ( 254 %(staff)s, 255 %(enc)s, 256 %(val)s, 257 %(curr)s, 258 %(billable)s 259 ) 260 RETURNING pk""" 261 args = { 262 'staff': pk_staff, 263 'enc': pk_encounter, 264 'val': billable['raw_amount'], 265 'curr': billable['currency'], 266 'billable': pk_billable 267 } 268 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 269 return cBillItem(aPK_obj = rows[0][0])270 #------------------------------------------------------------272 cmd = u'DELETE FROM bill.bill_item WHERE pk = %(pk)s AND fk_bill IS NULL' 273 args = {'pk': pk_bill_item} 274 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])275 276 #============================================================ 277 # bills 278 #------------------------------------------------------------ 279 _SQL_get_bill_fields = u"""SELECT * FROM bill.v_bills WHERE %s""" 280282 """Represents a bill""" 283 284 _cmd_fetch_payload = _SQL_get_bill_fields % u"pk_bill = %s" 285 _cmds_store_payload = [ 286 u"""UPDATE bill.bill SET 287 invoice_id = gm.nullify_empty_string(%(invoice_id)s), 288 close_date = %(close_date)s, 289 apply_vat = %(apply_vat)s, 290 comment = gm.nullify_empty_string(%(comment)s), 291 fk_receiver_identity = %(pk_receiver_identity)s, 292 fk_receiver_address = %(pk_receiver_address)s, 293 fk_doc = %(pk_doc)s 294 WHERE 295 pk = %(pk_bill)s 296 AND 297 xmin = %(xmin_bill)s 298 RETURNING 299 pk as pk_bill, 300 xmin as xmin_bill 301 """ 302 ] 303 _updatable_fields = [ 304 u'invoice_id', 305 u'pk_receiver_identity', 306 u'close_date', 307 u'apply_vat', 308 u'comment', 309 u'pk_receiver_address', 310 u'pk_doc' 311 ] 312 #--------------------------------------------------------422 #------------------------------------------------------------314 txt = u'%s [#%s]\n' % ( 315 gmTools.bool2subst ( 316 (self._payload[self._idx['close_date']] is None), 317 _('Open bill'), 318 _('Closed bill') 319 ), 320 self._payload[self._idx['pk_bill']] 321 ) 322 txt += _(' Invoice ID: %s\n') % self._payload[self._idx['invoice_id']] 323 324 if self._payload[self._idx['close_date']] is not None: 325 txt += _(' Closed: %s\n') % gmDateTime.pydt_strftime ( 326 self._payload[self._idx['close_date']], 327 '%Y %b %d', 328 accuracy = gmDateTime.acc_days 329 ) 330 331 if self._payload[self._idx['comment']] is not None: 332 txt += _(' Comment: %s\n') % self._payload[self._idx['comment']] 333 334 txt += _(' Bill value: %(curr)s%(val)s\n') % { 335 'curr': self._payload[self._idx['currency']], 336 'val': self._payload[self._idx['total_amount']] 337 } 338 339 if self._payload[self._idx['apply_vat']]: 340 txt += _(' VAT: %(perc_vat)s%% %(equals)s %(curr)s%(vat)s\n') % { 341 'perc_vat': self._payload[self._idx['percent_vat']], 342 'equals': gmTools.u_corresponds_to, 343 'curr': self._payload[self._idx['currency']], 344 'vat': self._payload[self._idx['total_vat']] 345 } 346 txt += _(' Value + VAT: %(curr)s%(val)s\n') % { 347 'curr': self._payload[self._idx['currency']], 348 'val': self._payload[self._idx['total_amount_with_vat']] 349 } 350 else: 351 txt += _(' VAT: does not apply\n') 352 353 if self._payload[self._idx['pk_bill_items']] is None: 354 txt += _(' Items billed: 0\n') 355 else: 356 txt += _(' Items billed: %s\n') % len(self._payload[self._idx['pk_bill_items']]) 357 txt += _(' Invoice: %s\n') % ( 358 gmTools.bool2subst ( 359 self._payload[self._idx['pk_doc']] is None, 360 _('not available'), 361 u'#%s' % self._payload[self._idx['pk_doc']] 362 ) 363 ) 364 txt += _(' Patient: #%s\n') % self._payload[self._idx['pk_patient']] 365 txt += gmTools.coalesce ( 366 self._payload[self._idx['pk_receiver_identity']], 367 u'', 368 _(' Receiver: #%s\n') 369 ) 370 if self._payload[self._idx['pk_receiver_address']] is not None: 371 txt += u'\n '.join(gmDemographicRecord.get_patient_address(pk_patient_address = self._payload[self._idx['pk_receiver_address']]).format()) 372 373 return txt374 #--------------------------------------------------------376 """Requires no pending changes within the bill itself.""" 377 # should check for item consistency first 378 conn = gmPG2.get_connection(readonly = False) 379 for item in items: 380 item['pk_bill'] = self._payload[self._idx['pk_bill']] 381 item.save(conn = conn) 382 conn.commit() 383 self.refetch_payload() # make sure aggregates are re-filled from view384 #--------------------------------------------------------386 return [ cBillItem(aPK_obj = pk) for pk in self._payload[self._idx['pk_bill_items']] ]387 388 bill_items = property(_get_bill_items, lambda x:x) 389 #--------------------------------------------------------391 if self._payload[self._idx['pk_doc']] is None: 392 return None 393 return gmDocuments.cDocument(aPK_obj = self._payload[self._idx['pk_doc']])394 395 invoice = property(_get_invoice, lambda x:x) 396 #--------------------------------------------------------398 if self._payload[self._idx['pk_receiver_address']] is None: 399 return None 400 return gmDemographicRecord.get_address_from_patient_address_pk ( 401 pk_patient_address = self._payload[self._idx['pk_receiver_address']] 402 )403 404 address = property(_get_address, lambda x:x) 405 #--------------------------------------------------------407 return gmDemographicRecord.get_patient_address_by_type ( 408 pk_patient = self._payload[self._idx['pk_patient']], 409 adr_type = u'billing' 410 )411 412 default_address = property(_get_default_address, lambda x:x) 413 #--------------------------------------------------------415 if self._payload[self._idx['pk_receiver_address']] is not None: 416 return True 417 adr = self.default_address 418 if adr is None: 419 return False 420 self['pk_receiver_address'] = adr['pk_lnk_person_org_address'] 421 return self.save_payload()424 425 args = {'pat': pk_patient} 426 where_parts = [u'true'] 427 428 if pk_patient is not None: 429 where_parts.append(u'pk_patient = %(pat)s') 430 431 if order_by is None: 432 order_by = u'' 433 else: 434 order_by = u' ORDER BY %s' % order_by 435 436 cmd = (_SQL_get_bill_fields % u' AND '.join(where_parts)) + order_by 437 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 438 return [ cBill(row = {'data': r, 'idx': idx, 'pk_field': 'pk_bill'}) for r in rows ]439 #------------------------------------------------------------441 442 args = {u'inv_id': invoice_id} 443 cmd = u""" 444 INSERT INTO bill.bill (invoice_id) 445 VALUES (gm.nullify_empty_string(%(inv_id)s)) 446 RETURNING pk 447 """ 448 rows, idx = gmPG2.run_rw_queries(link_obj = conn, queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False) 449 450 return cBill(aPK_obj = rows[0]['pk'])451 #------------------------------------------------------------453 args = {'pk': pk_bill} 454 cmd = u"DELETE FROM bill.bill WHERE pk = %(pk)s" 455 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 456 return True457 #------------------------------------------------------------ 460 #------------------------------------------------------------462 return u'GM%s / %s' % ( 463 pk_patient, 464 gmDateTime.pydt_strftime ( 465 gmDateTime.pydt_now_here(), 466 '%Y-%m-%d / %H%M%S' 467 ) 468 )469 #============================================================ 470 # main 471 #------------------------------------------------------------ 472 if __name__ == "__main__": 473 474 if len(sys.argv) < 2: 475 sys.exit() 476 477 if sys.argv[1] != 'test': 478 sys.exit() 479 480 # from Gnumed.pycommon import gmLog2 481 # from Gnumed.pycommon import gmI18N 482 # from Gnumed.business import gmPerson 483 484 # gmI18N.activate_locale() 485 ## gmDateTime.init() 486488 bills = get_bills(pk_patient = 12) 489 first_bill = bills[0] 490 print first_bill.default_address491493 print "--------------" 494 me = cBillable(aPK_obj=1) 495 fields = me.get_fields() 496 for field in fields: 497 print field, ':', me[field] 498 print "updatable:", me.get_updatable_fields()499 #me['vat']=4; me.store_payload() 500 #-------------------------------------------------- 501 #test_me() 502 test_default_address() 503
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Sat Aug 3 03:56:56 2013 | http://epydoc.sourceforge.net |