| 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"
30
32 """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 #--------------------------------------------------------
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 += _(' %s %s + %s%% VAT = %s %s\n') % (
70 self._payload[self._idx['raw_amount']],
71 self._payload[self._idx['currency']],
72 self._payload[self._idx['vat_multiplier']] * 100,
73 self._payload[self._idx['amount_with_vat']],
74 self._payload[self._idx['currency']]
75 )
76 txt += u' %s %s%s (%s)' % (
77 self._payload[self._idx['catalog_short']],
78 self._payload[self._idx['catalog_version']],
79 gmTools.coalesce(self._payload[self._idx['catalog_language']], u'', ' - %s'),
80 self._payload[self._idx['catalog_long']]
81 )
82 txt += gmTools.coalesce(self._payload[self._idx['comment']], u'', u'\n %s')
83
84 return txt
85 #--------------------------------------------------------
87 cmd = u'SELECT EXISTS(SELECT 1 FROM bill.bill_item WHERE fk_billable = %(pk)s LIMIT 1)'
88 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self._payload[self._idx['pk_billable']]}}])
89 return rows[0][0]
90
91 is_in_use = property(_get_is_in_use, lambda x:x)
92 #------------------------------------------------------------
94
95 if order_by is None:
96 order_by = u' ORDER BY catalog_long, catalog_version, billable_code'
97 else:
98 order_by = u' ORDER BY %s' % order_by
99
100 if active_only:
101 where = u'active IS true'
102 else:
103 where = u'true'
104
105 cmd = (_SQL_get_billable_fields % where) + order_by
106 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
107 return [ cBillable(row = {'data': r, 'idx': idx, 'pk_field': 'pk_billable'}) for r in rows ]
108 #------------------------------------------------------------
110 cmd = u"""
111 DELETE FROM ref.billable
112 WHERE
113 pk = %(pk)s
114 AND
115 NOT EXISTS (
116 SELECT 1 FROM bill.bill_item WHERE fk_billable = %(pk)s
117 )
118 """
119 args = {'pk': pk_billable}
120 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
121 #============================================================
122 # bill items
123 #------------------------------------------------------------
124 _SQL_fetch_bill_item_fields = u"SELECT * FROM bill.v_bill_items WHERE %s"
125
127
128 _cmd_fetch_payload = _SQL_fetch_bill_item_fields % u"pk_bill_item = %s"
129 _cmds_store_payload = [
130 u"""UPDATE bill.bill_item SET
131 fk_provider = %(pk_provider)s,
132 fk_encounter = %(pk_encounter_to_bill)s,
133 date_to_bill = %(raw_date_to_bill)s,
134 description = gm.nullify_empty_string(%(item_detail)s),
135 net_amount_per_unit = %(net_amount_per_unit)s,
136 currency = gm.nullify_empty_string(%(currency)s),
137 fk_bill = %(pk_bill)s,
138 unit_count = %(unit_count)s,
139 amount_multiplier = %(amount_multiplier)s
140 WHERE
141 pk = %(pk_bill_item)s
142 AND
143 xmin = %(xmin_bill_item)s
144 RETURNING
145 xmin AS xmin_bill_item
146 """]
147
148 _updatable_fields = [
149 'pk_provider',
150 'pk_encounter_to_bill',
151 'raw_date_to_bill',
152 'item_detail',
153 'net_amount_per_unit',
154 'currency',
155 'pk_bill',
156 'unit_count',
157 'amount_multiplier'
158 ]
159 #--------------------------------------------------------
161 txt = u'%s (%s %s%s) [#%s]\n' % (
162 gmTools.bool2subst(
163 self._payload[self._idx['pk_bill']] is None,
164 _('Open item'),
165 _('Billed item'),
166 ),
167 self._payload[self._idx['catalog_short']],
168 self._payload[self._idx['catalog_version']],
169 gmTools.coalesce(self._payload[self._idx['catalog_language']], u'', ' - %s'),
170 self._payload[self._idx['pk_bill_item']]
171 )
172 txt += u' %s: %s\n' % (
173 self._payload[self._idx['billable_code']],
174 self._payload[self._idx['billable_description']]
175 )
176 txt += gmTools.coalesce (
177 self._payload[self._idx['billable_comment']],
178 u'',
179 u' (%s)\n',
180 )
181 txt += gmTools.coalesce (
182 self._payload[self._idx['item_detail']],
183 u'',
184 _(' Details: %s\n'),
185 )
186
187 txt += u'\n'
188 txt += _(' %s of units: %s\n') % (
189 gmTools.u_numero,
190 self._payload[self._idx['unit_count']]
191 )
192 txt += _(' Amount per unit: %s %s (%s %s per catalog)\n') % (
193 self._payload[self._idx['net_amount_per_unit']],
194 self._payload[self._idx['currency']],
195 self._payload[self._idx['billable_amount']],
196 self._payload[self._idx['billable_currency']]
197 )
198 txt += _(' Amount multiplier: %s\n') % self._payload[self._idx['amount_multiplier']]
199 txt += _(' VAT would be: %s%% %s %s %s\n') % (
200 self._payload[self._idx['vat_multiplier']] * 100,
201 gmTools.u_corresponds_to,
202 self._payload[self._idx['vat']],
203 self._payload[self._idx['currency']]
204 )
205
206 txt += u'\n'
207 txt += _(' Charge date: %s') % gmDateTime.pydt_strftime (
208 self._payload[self._idx['date_to_bill']],
209 '%Y %b %d',
210 accuracy = gmDateTime.acc_days
211 )
212 bill = self.bill
213 if bill is not None:
214 txt += _('\n On bill: %s') % bill['invoice_id']
215
216 return txt
217 #--------------------------------------------------------
219 return cBillable(aPK_obj = self._payload[self._idx['pk_billable']])
220
221 billable = property(_get_billable, lambda x:x)
222 #--------------------------------------------------------
224 if self._payload[self._idx['pk_bill']] is None:
225 return None
226 return cBill(aPK_obj = self._payload[self._idx['pk_bill']])
227
228 bill = property(_get_bill, lambda x:x)
229 #--------------------------------------------------------
232
233 is_in_use = property(_get_is_in_use, lambda x:x)
234 #------------------------------------------------------------
236 if non_invoiced_only:
237 cmd = _SQL_fetch_bill_item_fields % u"pk_patient = %(pat)s AND pk_bill IS NULL"
238 else:
239 cmd = _SQL_fetch_bill_item_fields % u"pk_patient = %(pat)s"
240 args = {'pat': pk_patient}
241 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
242 return [ cBillItem(row = {'data': r, 'idx': idx, 'pk_field': 'pk_bill_item'}) for r in rows ]
243 #------------------------------------------------------------
245
246 billable = cBillable(aPK_obj = pk_billable)
247 cmd = u"""
248 INSERT INTO bill.bill_item (
249 fk_provider,
250 fk_encounter,
251 net_amount_per_unit,
252 currency,
253 fk_billable
254 ) VALUES (
255 %(staff)s,
256 %(enc)s,
257 %(val)s,
258 %(curr)s,
259 %(billable)s
260 )
261 RETURNING pk"""
262 args = {
263 'staff': pk_staff,
264 'enc': pk_encounter,
265 'val': billable['raw_amount'],
266 'curr': billable['currency'],
267 'billable': pk_billable
268 }
269 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
270 return cBillItem(aPK_obj = rows[0][0])
271 #------------------------------------------------------------
273 cmd = u'DELETE FROM bill.bill_item WHERE pk = %(pk)s AND fk_bill IS NULL'
274 args = {'pk': pk_bill_item}
275 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
276
277 #============================================================
278 # bills
279 #------------------------------------------------------------
280 _SQL_get_bill_fields = u"""SELECT * FROM bill.v_bills WHERE %s"""
281
283 """Represents a bill"""
284
285 _cmd_fetch_payload = _SQL_get_bill_fields % u"pk_bill = %s"
286 _cmds_store_payload = [
287 u"""UPDATE bill.bill SET
288 invoice_id = gm.nullify_empty_string(%(invoice_id)s),
289 close_date = %(close_date)s,
290 apply_vat = %(apply_vat)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'pk_receiver_address',
309 u'pk_doc'
310 ]
311 #--------------------------------------------------------
313 txt = u'%s [#%s]\n' % (
314 gmTools.bool2subst (
315 (self._payload[self._idx['close_date']] is None),
316 _('Open bill'),
317 _('Closed bill')
318 ),
319 self._payload[self._idx['pk_bill']]
320 )
321 txt += _(' Invoice ID: %s\n') % self._payload[self._idx['invoice_id']]
322 if self._payload[self._idx['close_date']] is not None:
323 txt += _(' Closed: %s\n') % gmDateTime.pydt_strftime (
324 self._payload[self._idx['close_date']],
325 '%Y %b %d',
326 accuracy = gmDateTime.acc_days
327 )
328 txt += _(' Bill value: %s %s\n') % (
329 self._payload[self._idx['total_amount']],
330 self._payload[self._idx['currency']]
331 )
332 if self._payload[self._idx['apply_vat']]:
333 txt += _(' VAT: %s%% %s %s %s\n') % (
334 self._payload[self._idx['percent_vat']],
335 gmTools.u_corresponds_to,
336 self._payload[self._idx['total_vat']],
337 self._payload[self._idx['currency']]
338 )
339 txt += _(' Value + VAT: %s %s\n') % (
340 self._payload[self._idx['total_amount_with_vat']],
341 self._payload[self._idx['currency']]
342 )
343 else:
344 txt += _(' VAT: does not apply\n')
345 txt += _(' Items billed: %s\n') % len(self._payload[self._idx['pk_bill_items']])
346 txt += _(' Invoice: %s\n') % (
347 gmTools.bool2subst (
348 self._payload[self._idx['pk_doc']] is None,
349 _('not available'),
350 u'#%s' % self._payload[self._idx['pk_doc']]
351 )
352 )
353 txt += _(' Patient: #%s\n') % self._payload[self._idx['pk_patient']]
354 txt += gmTools.coalesce (
355 self._payload[self._idx['pk_receiver_identity']],
356 u'',
357 _(' Receiver: #%s\n')
358 )
359 if self._payload[self._idx['pk_receiver_address']] is not None:
360 txt += u'\n '.join(gmDemographicRecord.get_patient_address(pk_patient_address = self._payload[self._idx['pk_receiver_address']]).format())
361
362 return txt
363 #--------------------------------------------------------
365 """Requires no pending changes within the bill itself."""
366 # should check for item consistency first
367 conn = gmPG2.get_connection(readonly = False)
368 for item in items:
369 item['pk_bill'] = self._payload[self._idx['pk_bill']]
370 item.save(conn = conn)
371 conn.commit()
372 self.refetch_payload() # make sure aggregates are re-filled from view
373 #--------------------------------------------------------
375 return [ cBillItem(aPK_obj = pk) for pk in self._payload[self._idx['pk_bill_items']] ]
376
377 bill_items = property(_get_bill_items, lambda x:x)
378 #--------------------------------------------------------
380 if self._payload[self._idx['pk_doc']] is None:
381 return None
382 return gmDocuments.cDocument(aPK_obj = self._payload[self._idx['pk_doc']])
383
384 invoice = property(_get_invoice, lambda x:x)
385 #--------------------------------------------------------
387 if self._payload[self._idx['pk_receiver_address']] is None:
388 return None
389 return gmDemographicRecord.get_address_from_patient_address_pk (
390 pk_patient_address = self._payload[self._idx['pk_receiver_address']]
391 )
392
393 address = property(_get_address, lambda x:x)
394 #--------------------------------------------------------
396 return gmDemographicRecord.get_patient_address_by_type (
397 pk_patient = self._payload[self._idx['pk_patient']],
398 adr_type = u'billing'
399 )
400
401 default_address = property(_get_default_address, lambda x:x)
402 #--------------------------------------------------------
404 if self._payload[self._idx['pk_receiver_address']] is not None:
405 return True
406 adr = self.default_address
407 if adr is None:
408 return False
409 self['pk_receiver_address'] = adr['pk_lnk_person_org_address']
410 return self.save_payload()
411 #------------------------------------------------------------
413
414 args = {'pat': pk_patient}
415 where_parts = [u'true']
416
417 if pk_patient is not None:
418 where_parts.append(u'pk_patient = %(pat)s')
419
420 if order_by is None:
421 order_by = u''
422 else:
423 order_by = u' ORDER BY %s' % order_by
424
425 cmd = (_SQL_get_bill_fields % u' AND '.join(where_parts)) + order_by
426 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
427 return [ cBill(row = {'data': r, 'idx': idx, 'pk_field': 'pk_bill'}) for r in rows ]
428 #------------------------------------------------------------
430
431 args = {u'inv_id': invoice_id}
432 cmd = u"""
433 INSERT INTO bill.bill (invoice_id)
434 VALUES (gm.nullify_empty_string(%(inv_id)s))
435 RETURNING pk
436 """
437 rows, idx = gmPG2.run_rw_queries(link_obj = conn, queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
438
439 return cBill(aPK_obj = rows[0]['pk'])
440 #------------------------------------------------------------
442 args = {'pk': pk_bill}
443 cmd = u"DELETE FROM bill.bill WHERE pk = %(pk)s"
444 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
445 return True
446 #------------------------------------------------------------
449 #------------------------------------------------------------
451 return u'GM%s / %s' % (
452 pk_patient,
453 gmDateTime.pydt_strftime (
454 gmDateTime.pydt_now_here(),
455 '%Y-%m-%d / %H%M%S'
456 )
457 )
458 #============================================================
459 # main
460 #------------------------------------------------------------
461 if __name__ == "__main__":
462
463 if len(sys.argv) < 2:
464 sys.exit()
465
466 if sys.argv[1] != 'test':
467 sys.exit()
468
469 # from Gnumed.pycommon import gmLog2
470 # from Gnumed.pycommon import gmI18N
471 # from Gnumed.business import gmPerson
472
473 # gmI18N.activate_locale()
474 ## gmDateTime.init()
475
477 bills = get_bills(pk_patient = 12)
478 first_bill = bills[0]
479 print first_bill.default_address
480
482 print "--------------"
483 me = cBillable(aPK_obj=1)
484 fields = me.get_fields()
485 for field in fields:
486 print field, ':', me[field]
487 print "updatable:", me.get_updatable_fields()
488 #me['vat']=4; me.store_payload()
489 #--------------------------------------------------
490 #test_me()
491 test_default_address()
492
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jun 25 03:58:30 2012 | http://epydoc.sourceforge.net |