| Home | Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: utf-8 -*-
2 """GNUmed billing handling widgets."""
3
4 #================================================================
5 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
6 __license__ = "GPL v2 or later"
7
8 import logging
9 import sys
10
11
12 import wx
13
14
15 if __name__ == '__main__':
16 sys.path.insert(0, '../../')
17 from Gnumed.pycommon import gmTools
18 from Gnumed.pycommon import gmDateTime
19 from Gnumed.pycommon import gmMatchProvider
20 from Gnumed.pycommon import gmDispatcher
21 from Gnumed.pycommon import gmPG2
22 from Gnumed.pycommon import gmCfg
23 from Gnumed.pycommon import gmCfg2
24 from Gnumed.pycommon import gmPrinting
25 from Gnumed.pycommon import gmNetworkTools
26
27 from Gnumed.business import gmBilling
28 from Gnumed.business import gmPerson
29 from Gnumed.business import gmStaff
30 from Gnumed.business import gmDocuments
31 from Gnumed.business import gmPraxis
32 from Gnumed.business import gmForms
33 from Gnumed.business import gmDemographicRecord
34
35 from Gnumed.wxpython import gmListWidgets
36 from Gnumed.wxpython import gmRegetMixin
37 from Gnumed.wxpython import gmPhraseWheel
38 from Gnumed.wxpython import gmGuiHelpers
39 from Gnumed.wxpython import gmEditArea
40 from Gnumed.wxpython import gmPersonContactWidgets
41 from Gnumed.wxpython import gmPatSearchWidgets
42 from Gnumed.wxpython import gmMacro
43 from Gnumed.wxpython import gmFormWidgets
44 from Gnumed.wxpython import gmDocumentWidgets
45 from Gnumed.wxpython import gmDataPackWidgets
46
47
48 _log = logging.getLogger('gm.ui')
49
50 #================================================================
52 ea = cBillableEAPnl(parent, -1)
53 ea.data = billable
54 ea.mode = gmTools.coalesce(billable, 'new', 'edit')
55 dlg = gmEditArea.cGenericEditAreaDlg2 (
56 parent = parent,
57 id = -1,
58 edit_area = ea,
59 single_entry = gmTools.bool2subst((billable is None), False, True)
60 )
61 dlg.SetTitle(gmTools.coalesce(billable, _('Adding new billable'), _('Editing billable')))
62 if dlg.ShowModal() == wx.ID_OK:
63 dlg.Destroy()
64 return True
65 dlg.Destroy()
66 return False
67
68 #----------------------------------------------------------------
70
71 if parent is None:
72 parent = wx.GetApp().GetTopWindow()
73
74 #------------------------------------------------------------
75 def edit(billable=None):
76 return edit_billable(parent = parent, billable = billable)
77 #------------------------------------------------------------
78 def delete(billable):
79 if billable.is_in_use:
80 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this billable item. It is in use.'), beep = True)
81 return False
82 return gmBilling.delete_billable(pk_billable = billable['pk_billable'])
83 #------------------------------------------------------------
84 def get_tooltip(item):
85 if item is None:
86 return None
87 return item.format()
88 #------------------------------------------------------------
89 def refresh(lctrl):
90 billables = gmBilling.get_billables()
91 items = [ [
92 b['billable_code'],
93 b['billable_description'],
94 '%(currency)s%(raw_amount)s' % b,
95 '%s (%s)' % (b['catalog_short'], b['catalog_version']),
96 gmTools.coalesce(b['comment'], ''),
97 b['pk_billable']
98 ] for b in billables ]
99 lctrl.set_string_items(items)
100 lctrl.set_data(billables)
101 #------------------------------------------------------------
102 def manage_data_packs(billable):
103 gmDataPackWidgets.manage_data_packs(parent = parent)
104 return True
105 #------------------------------------------------------------
106 def browse_catalogs(billable):
107 dbcfg = gmCfg.cCfgSQL()
108 url = dbcfg.get2 (
109 option = 'external.urls.schedules_of_fees',
110 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
111 bias = 'user',
112 default = 'http://www.e-bis.de/goae/defaultFrame.htm'
113 )
114 gmNetworkTools.open_url_in_browser(url = url)
115 return False
116 #------------------------------------------------------------
117 msg = _('\nThese are the items for billing registered with GNUmed.\n')
118
119 gmListWidgets.get_choices_from_list (
120 parent = parent,
121 msg = msg,
122 caption = _('Showing billable items.'),
123 columns = [_('Code'), _('Description'), _('Value'), _('Catalog'), _('Comment'), '#'],
124 single_selection = True,
125 new_callback = edit,
126 edit_callback = edit,
127 delete_callback = delete,
128 refresh_callback = refresh,
129 middle_extra_button = (
130 _('Data packs'),
131 _('Browse and install billing catalog (schedule of fees) data packs'),
132 manage_data_packs
133 ),
134 right_extra_button = (
135 _('Catalogs (WWW)'),
136 _('Browse billing catalogs (schedules of fees) on the web'),
137 browse_catalogs
138 ),
139 list_tooltip_callback = get_tooltip
140 )
141
142 #----------------------------------------------------------------
144
146 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
147 query = """
148 SELECT -- DISTINCT ON (label)
149 r_vb.pk_billable
150 AS data,
151 r_vb.billable_code || ': ' || r_vb.billable_description || ' (' || r_vb.catalog_short || ' - ' || r_vb.catalog_version || ')'
152 AS list_label,
153 r_vb.billable_code || ' (' || r_vb.catalog_short || ' - ' || r_vb.catalog_version || ')'
154 AS field_label
155 FROM
156 ref.v_billables r_vb
157 WHERE
158 r_vb.active
159 AND (
160 r_vb.billable_code %(fragment_condition)s
161 OR
162 r_vb.billable_description %(fragment_condition)s
163 )
164 ORDER BY list_label
165 LIMIT 20
166 """
167 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
168 mp.setThresholds(1, 2, 4)
169 self.matcher = mp
170 #------------------------------------------------------------
173 #------------------------------------------------------------
175 if self.GetData() is None:
176 return None
177 billable = gmBilling.cBillable(aPK_obj = list(self._data.values())[0]['data'])
178 return billable.format()
179 #------------------------------------------------------------
181 val = '%s (%s - %s)' % (
182 instance['billable_code'],
183 instance['catalog_short'],
184 instance['catalog_version']
185 )
186 self.SetText(value = val, data = instance['pk_billable'])
187 #------------------------------------------------------------
190
191 #----------------------------------------------------------------
192 from Gnumed.wxGladeWidgets import wxgBillableEAPnl
193
195
197
198 try:
199 data = kwargs['billable']
200 del kwargs['billable']
201 except KeyError:
202 data = None
203
204 wxgBillableEAPnl.wxgBillableEAPnl.__init__(self, *args, **kwargs)
205 gmEditArea.cGenericEditAreaMixin.__init__(self)
206
207 self.mode = 'new'
208 self.data = data
209 if data is not None:
210 self.mode = 'edit'
211
212 #self.__init_ui()
213 #----------------------------------------------------------------
214 # def __init_ui(self):
215 # # adjust phrasewheels etc
216 #----------------------------------------------------------------
217 # generic Edit Area mixin API
218 #----------------------------------------------------------------
220
221 validity = True
222
223 vat = self._TCTRL_vat.GetValue().strip()
224 if vat == '':
225 self.display_tctrl_as_valid(tctrl = self._TCTRL_vat, valid = True)
226 else:
227 success, vat = gmTools.input2decimal(initial = vat)
228 if success:
229 self.display_tctrl_as_valid(tctrl = self._TCTRL_vat, valid = True)
230 else:
231 validity = False
232 self.display_tctrl_as_valid(tctrl = self._TCTRL_vat, valid = False)
233 self.status_message = _('VAT must be empty or a number.')
234 self._TCTRL_vat.SetFocus()
235
236 currency = self._TCTRL_currency.GetValue().strip()
237 if currency == '':
238 validity = False
239 self.display_tctrl_as_valid(tctrl = self._TCTRL_currency, valid = False)
240 self.status_message = _('Currency is missing.')
241 self._TCTRL_currency.SetFocus()
242 else:
243 self.display_tctrl_as_valid(tctrl = self._TCTRL_currency, valid = True)
244
245 success, val = gmTools.input2decimal(initial = self._TCTRL_amount.GetValue())
246 if success:
247 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
248 else:
249 validity = False
250 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
251 self.status_message = _('Value is missing.')
252 self._TCTRL_amount.SetFocus()
253
254 if self._TCTRL_description.GetValue().strip() == '':
255 validity = False
256 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = False)
257 self.status_message = _('Description is missing.')
258 self._TCTRL_description.SetFocus()
259 else:
260 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = True)
261
262 if self._PRW_coding_system.GetData() is None:
263 validity = False
264 self._PRW_coding_system.display_as_valid(False)
265 self.status_message = _('Coding system is missing.')
266 self._PRW_coding_system.SetFocus()
267 else:
268 self._PRW_coding_system.display_as_valid(True)
269
270 if self._TCTRL_code.GetValue().strip() == '':
271 validity = False
272 self.display_tctrl_as_valid(tctrl = self._TCTRL_code, valid = False)
273 self.status_message = _('Code is missing.')
274 self._TCTRL_code.SetFocus()
275 else:
276 self.display_tctrl_as_valid(tctrl = self._TCTRL_code, valid = True)
277
278 return validity
279 #----------------------------------------------------------------
281 data = gmBilling.create_billable (
282 code = self._TCTRL_code.GetValue().strip(),
283 term = self._TCTRL_description.GetValue().strip(),
284 data_source = self._PRW_coding_system.GetData(),
285 return_existing = False
286 )
287 if data is None:
288 self.status_message = _('Billable already exists.')
289 return False
290
291 val = self._TCTRL_amount.GetValue().strip()
292 if val != '':
293 tmp, val = gmTools.input2decimal(val)
294 data['raw_amount'] = val
295 val = self._TCTRL_currency.GetValue().strip()
296 if val != '':
297 data['currency'] = val
298 vat = self._TCTRL_vat.GetValue().strip()
299 if vat != '':
300 tmp, vat = gmTools.input2decimal(vat)
301 data['vat_multiplier'] = vat / 100
302 data['comment'] = self._TCTRL_comment.GetValue().strip()
303 data['active'] = self._CHBOX_active.GetValue()
304
305 data.save()
306
307 self.data = data
308
309 return True
310 #----------------------------------------------------------------
312 self.data['billable_description'] = self._TCTRL_description.GetValue().strip()
313 tmp, self.data['raw_amount'] = gmTools.input2decimal(self._TCTRL_amount.GetValue())
314 self.data['currency'] = self._TCTRL_currency.GetValue().strip()
315 vat = self._TCTRL_vat.GetValue().strip()
316 if vat == '':
317 vat = 0
318 else:
319 tmp, vat = gmTools.input2decimal(vat)
320 self.data['vat_multiplier'] = vat / 100
321 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
322 self.data['active'] = self._CHBOX_active.GetValue()
323 self.data.save()
324 return True
325 #----------------------------------------------------------------
327 self._TCTRL_code.SetValue('')
328 self._PRW_coding_system.SetText('', None)
329 self._TCTRL_description.SetValue('')
330 self._TCTRL_amount.SetValue('')
331 self._TCTRL_currency.SetValue('')
332 self._TCTRL_vat.SetValue('')
333 self._TCTRL_comment.SetValue('')
334 self._CHBOX_active.SetValue(True)
335
336 self._TCTRL_code.SetFocus()
337 #----------------------------------------------------------------
340 #----------------------------------------------------------------
342 self._TCTRL_code.SetValue(self.data['billable_code'])
343 self._TCTRL_code.Enable(False)
344 self._PRW_coding_system.SetText('%s (%s)' % (self.data['catalog_short'], self.data['catalog_version']), self.data['pk_data_source'])
345 self._PRW_coding_system.Enable(False)
346 self._TCTRL_description.SetValue(self.data['billable_description'])
347 self._TCTRL_amount.SetValue('%s' % self.data['raw_amount'])
348 self._TCTRL_currency.SetValue(self.data['currency'])
349 self._TCTRL_vat.SetValue('%s' % (self.data['vat_multiplier'] * 100))
350 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
351 self._CHBOX_active.SetValue(self.data['active'])
352
353 self._TCTRL_description.SetFocus()
354 #----------------------------------------------------------------
355
356 #================================================================
357 # invoice related widgets
358 #----------------------------------------------------------------
360
361 if parent is None:
362 parent = wx.GetApp().GetTopWindow()
363
364 template = gmFormWidgets.manage_form_templates (
365 parent = parent,
366 template_types = ['invoice']
367 )
368
369 if template is None:
370 gmDispatcher.send(signal = 'statustext', msg = _('No invoice template configured.'), beep = True)
371 return None
372
373 if template['engine'] not in ['L', 'X']:
374 gmDispatcher.send(signal = 'statustext', msg = _('No invoice template configured.'), beep = True)
375 return None
376
377 if with_vat:
378 option = 'form_templates.invoice_with_vat'
379 else:
380 option = 'form_templates.invoice_no_vat'
381
382 dbcfg = gmCfg.cCfgSQL()
383 dbcfg.set (
384 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
385 option = option,
386 value = '%s - %s' % (template['name_long'], template['external_version'])
387 )
388
389 return template
390 #----------------------------------------------------------------
392
393 dbcfg = gmCfg.cCfgSQL()
394 if with_vat:
395 option = 'form_templates.invoice_with_vat'
396 else:
397 option = 'form_templates.invoice_no_vat'
398
399 template = dbcfg.get2 (
400 option = option,
401 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
402 bias = 'user'
403 )
404
405 if template is None:
406 template = configure_invoice_template(parent = parent, with_vat = with_vat)
407 if template is None:
408 gmGuiHelpers.gm_show_error (
409 aMessage = _('There is no invoice template configured.'),
410 aTitle = _('Getting invoice template')
411 )
412 return None
413 else:
414 try:
415 name, ver = template.split(' - ')
416 except:
417 _log.exception('problem splitting invoice template name [%s]', template)
418 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading invoice template.'), beep = True)
419 return None
420 template = gmForms.get_form_template(name_long = name, external_version = ver)
421 if template is None:
422 gmGuiHelpers.gm_show_error (
423 aMessage = _('Cannot load invoice template [%s - %s]') % (name, ver),
424 aTitle = _('Getting invoice template')
425 )
426 return None
427
428 return template
429
430 #================================================================
431 # per-patient bill related widgets
432 #----------------------------------------------------------------
434
435 if bill is None:
436 # manually creating bills is not yet supported
437 return
438
439 ea = cBillEAPnl(parent, -1)
440 ea.data = bill
441 ea.mode = gmTools.coalesce(bill, 'new', 'edit')
442 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
443 dlg.SetTitle(gmTools.coalesce(bill, _('Adding new bill'), _('Editing bill')))
444 if dlg.ShowModal() == wx.ID_OK:
445 dlg.Destroy()
446 return True
447 dlg.Destroy()
448 return False
449 #----------------------------------------------------------------
451
452 if len(bill_items) == 0:
453 return None
454
455 item = bill_items[0]
456 currency = item['currency']
457 vat = item['vat_multiplier']
458 pat = item['pk_patient']
459
460 # check item consistency
461 has_errors = False
462 for item in bill_items:
463 if (item['currency'] != currency) or (
464 item['vat_multiplier'] != vat) or (
465 item['pk_patient'] != pat
466 ):
467 msg = _(
468 'All items to be included with a bill must\n'
469 'coincide on currency, VAT, and patient.\n'
470 '\n'
471 'This item does not:\n'
472 '\n'
473 '%s\n'
474 ) % item.format()
475 has_errors = True
476
477 if item['pk_bill'] is not None:
478 msg = _(
479 'This item is already invoiced:\n'
480 '\n'
481 '%s\n'
482 '\n'
483 'Cannot put it on a second bill.'
484 ) % item.format()
485 has_errors = True
486
487 if has_errors:
488 gmGuiHelpers.gm_show_warning(aTitle = _('Checking invoice items'), aMessage = msg)
489 return None
490
491 # create bill
492 bill = gmBilling.create_bill(invoice_id = gmBilling.get_invoice_id(pk_patient = pat))
493 _log.info('created bill [%s]', bill['invoice_id'])
494 bill.add_items(items = bill_items)
495 bill.set_missing_address_from_default()
496
497 return bill
498
499 #----------------------------------------------------------------
501
502 bill_patient_not_active = False
503 # do we have a current patient ?
504 curr_pat = gmPerson.gmCurrentPatient()
505 if curr_pat.connected:
506 # is the bill about the current patient, too ?
507 # (because that's what the new invoice would get
508 # created for and attached to)
509 if curr_pat.ID != bill['pk_patient']:
510 bill_patient_not_active = True
511 else:
512 bill_patient_not_active = True
513
514 # FIXME: could ask whether to set fk_receiver_identity
515 # FIXME: but this would need enabling the bill EA to edit same
516 if bill_patient_not_active:
517 activate_patient = gmGuiHelpers.gm_show_question (
518 title = _('Creating invoice'),
519 question = _(
520 'Cannot find an existing invoice PDF for this bill.\n'
521 '\n'
522 'Active patient: %s\n'
523 'Patient on bill: #%s\n'
524 '\n'
525 'Activate patient on bill so invoice PDF can be created ?'
526 ) % (
527 gmTools.coalesce(curr_pat.ID, '', '#%s'),
528 bill['pk_patient']
529 )
530 )
531 if not activate_patient:
532 return False
533 if not gmPatSearchWidgets.set_active_patient(patient = bill['pk_patient']):
534 gmGuiHelpers.gm_show_error (
535 aTitle = _('Creating invoice'),
536 aMessage = _('Cannot activate patient #%s.') % bill['pk_patient']
537 )
538 return False
539
540 if None in [ bill['close_date'], bill['pk_receiver_address'], bill['apply_vat'] ]:
541 edit_bill(parent = parent, bill = bill, single_entry = True)
542 # cannot invoice open bills
543 if bill['close_date'] is None:
544 _log.error('cannot create invoice from bill, bill not closed')
545 gmGuiHelpers.gm_show_warning (
546 aTitle = _('Creating invoice'),
547 aMessage = _(
548 'Cannot create invoice from bill.\n'
549 '\n'
550 'The bill is not closed.'
551 )
552 )
553 return False
554 # cannot create invoice if no receiver address
555 if bill['pk_receiver_address'] is None:
556 _log.error('cannot create invoice from bill, lacking receiver address')
557 gmGuiHelpers.gm_show_warning (
558 aTitle = _('Creating invoice'),
559 aMessage = _(
560 'Cannot create invoice from bill.\n'
561 '\n'
562 'There is no receiver address.'
563 )
564 )
565 return False
566 # cannot create invoice if applying VAT is undecided
567 if bill['apply_vat'] is None:
568 _log.error('cannot create invoice from bill, apply_vat undecided')
569 gmGuiHelpers.gm_show_warning (
570 aTitle = _('Creating invoice'),
571 aMessage = _(
572 'Cannot create invoice from bill.\n'
573 '\n'
574 'You must decide on whether to apply VAT.'
575 )
576 )
577 return False
578
579 # find template
580 template = get_invoice_template(parent = parent, with_vat = bill['apply_vat'])
581 if template is None:
582 gmGuiHelpers.gm_show_warning (
583 aTitle = _('Creating invoice'),
584 aMessage = _(
585 'Cannot create invoice from bill\n'
586 'without an invoice template.'
587 )
588 )
589 return False
590
591 # process template
592 try:
593 invoice = template.instantiate()
594 except KeyError:
595 _log.exception('cannot instantiate invoice template [%s]', template)
596 gmGuiHelpers.gm_show_error (
597 aMessage = _('Invalid invoice template [%s - %s (%s)]') % (name, ver, template['engine']),
598 aTitle = _('Printing medication list')
599 )
600 return False
601
602 ph = gmMacro.gmPlaceholderHandler()
603 #ph.debug = True
604 ph.set_cache_value('bill', bill)
605 invoice.substitute_placeholders(data_source = ph)
606 ph.unset_cache_value('bill')
607 pdf_name = invoice.generate_output()
608 if pdf_name is None:
609 gmGuiHelpers.gm_show_error (
610 aMessage = _('Error generating invoice PDF.'),
611 aTitle = _('Creating invoice')
612 )
613 return False
614
615 # keep a copy
616 if keep_a_copy:
617 files2import = []
618 files2import.extend(invoice.final_output_filenames)
619 files2import.extend(invoice.re_editable_filenames)
620 doc = gmDocumentWidgets.save_files_as_new_document (
621 parent = parent,
622 filenames = files2import,
623 document_type = template['instance_type'],
624 review_as_normal = True,
625 reference = bill['invoice_id'],
626 pk_org_unit = gmPraxis.gmCurrentPraxisBranch()['pk_org_unit']
627 )
628 bill['pk_doc'] = doc['pk_doc']
629 bill.save()
630
631 if not print_it:
632 return True
633
634 # print template
635 _cfg = gmCfg2.gmCfgData()
636 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'invoice', verbose = _cfg.get(option = 'debug'))
637 if not printed:
638 gmGuiHelpers.gm_show_error (
639 aMessage = _('Error printing the invoice.'),
640 aTitle = _('Printing invoice')
641 )
642 return True
643
644 return True
645
646 #----------------------------------------------------------------
648
649 if parent is None:
650 parent = wx.GetApp().GetTopWindow()
651
652 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
653 parent, -1,
654 caption = _('Deleting bill'),
655 question = _(
656 'When deleting the bill [%s]\n'
657 'do you want to keep its items (effectively \"unbilling\" them)\n'
658 'or do you want to also delete the bill items from the patient ?\n'
659 ) % bill['invoice_id'],
660 button_defs = [
661 {'label': _('Delete + keep'), 'tooltip': _('Delete the bill but keep ("unbill") its items.'), 'default': True},
662 {'label': _('Delete all'), 'tooltip': _('Delete both the bill and its items from the patient.')}
663 ],
664 show_checkbox = True,
665 checkbox_msg = _('Also remove invoice PDF'),
666 checkbox_tooltip = _('Also remove the invoice PDF from the document archive (because it will not correspond to the bill anymore).')
667 )
668 button_pressed = dlg.ShowModal()
669 delete_invoice = dlg.checkbox_is_checked()
670 dlg.Destroy()
671
672 if button_pressed == wx.ID_CANCEL:
673 return False
674
675 delete_items = (button_pressed == wx.ID_NO)
676
677 if delete_invoice:
678 if bill['pk_doc'] is not None:
679 gmDocuments.delete_document (
680 document_id = bill['pk_doc'],
681 encounter_id = gmPerson.cPatient(aPK_obj = bill['pk_patient']).emr.active_encounter['pk_encounter']
682 )
683
684 items = bill['pk_bill_items']
685 success = gmBilling.delete_bill(pk_bill = bill['pk_bill'])
686 if delete_items:
687 for item in items:
688 gmBilling.delete_bill_item(pk_bill_item = item)
689
690 return success
691
692 #----------------------------------------------------------------
694
695 if bill is None:
696 return False
697
698 list_data = bill.bill_items
699 if len(list_data) == 0:
700 return False
701
702 if parent is None:
703 parent = wx.GetApp().GetTopWindow()
704
705 list_items = [ [
706 gmDateTime.pydt_strftime(b['date_to_bill'], '%Y %b %d', accuracy = gmDateTime.acc_days),
707 b['unit_count'],
708 '%s: %s%s' % (b['billable_code'], b['billable_description'], gmTools.coalesce(b['item_detail'], '', ' - %s')),
709 '%(curr)s %(total_val)s (%(count)s %(x)s %(unit_val)s%(x)s%(val_multiplier)s)' % {
710 'curr': b['currency'],
711 'total_val': b['total_amount'],
712 'count': b['unit_count'],
713 'x': gmTools.u_multiply,
714 'unit_val': b['net_amount_per_unit'],
715 'val_multiplier': b['amount_multiplier']
716 },
717 '%(curr)s%(vat)s (%(perc_vat)s%%)' % {
718 'vat': b['vat'],
719 'curr': b['currency'],
720 'perc_vat': b['vat_multiplier'] * 100
721 },
722 '%s (%s)' % (b['catalog_short'], b['catalog_version']),
723 b['pk_bill_item']
724 ] for b in list_data ]
725
726 msg = _('Select the items you want to remove from bill [%s]:\n') % bill['invoice_id']
727 items2remove = gmListWidgets.get_choices_from_list (
728 parent = parent,
729 msg = msg,
730 caption = _('Removing items from bill'),
731 columns = [_('Date'), _('Count'), _('Description'), _('Value'), _('VAT'), _('Catalog'), '#'],
732 single_selection = False,
733 choices = list_items,
734 data = list_data
735 )
736
737 if items2remove is None:
738 return False
739
740 if len(items2remove) == len(list_items):
741 gmGuiHelpers.gm_show_info (
742 title = _('Removing items from bill'),
743 info = _(
744 'Cannot remove all items from a bill because\n'
745 'GNUmed does not support empty bills.\n'
746 '\n'
747 'You must delete the bill itself if you want to\n'
748 'remove all items (at which point you can opt to\n'
749 'keep the items and only delete the bill).'
750 )
751 )
752 return False
753
754 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
755 parent, -1,
756 caption = _('Removing items from bill'),
757 question = _(
758 '%s items selected from bill [%s]\n'
759 '\n'
760 'Do you want to only remove the selected items\n'
761 'from the bill ("unbill" them) or do you want\n'
762 'to delete them entirely from the patient ?\n'
763 '\n'
764 'Note that neither action is reversible.'
765 ) % (
766 len(items2remove),
767 bill['invoice_id']
768 ),
769 button_defs = [
770 {'label': _('"Unbill"'), 'tooltip': _('Only "unbill" items (remove from bill but do not delete from patient).'), 'default': True},
771 {'label': _('Delete'), 'tooltip': _('Completely delete items from the patient.')}
772 ],
773 show_checkbox = True,
774 checkbox_msg = _('Also remove invoice PDF'),
775 checkbox_tooltip = _('Also remove the invoice PDF from the document archive (because it will not correspond to the bill anymore).')
776 )
777 button_pressed = dlg.ShowModal()
778 delete_invoice = dlg.checkbox_is_checked()
779 dlg.Destroy()
780
781 if button_pressed == wx.ID_CANCEL:
782 return False
783
784 # remember this because unlinking/deleting the items
785 # will remove the patient PK from the bill
786 pk_patient = bill['pk_patient']
787
788 for item in items2remove:
789 item['pk_bill'] = None
790 item.save()
791 if button_pressed == wx.ID_NO:
792 gmBilling.delete_bill_item(pk_bill_item = item['pk_bill_item'])
793
794 if delete_invoice:
795 if bill['pk_doc'] is not None:
796 gmDocuments.delete_document (
797 document_id = bill['pk_doc'],
798 encounter_id = gmPerson.cPatient(aPK_obj = pk_patient).emr.active_encounter['pk_encounter']
799 )
800
801 # delete bill, too, if empty
802 if len(bill.bill_items) == 0:
803 gmBilling.delete_bill(pk_bill = bill['pk_bill'])
804
805 return True
806
807 #----------------------------------------------------------------
809
810 if parent is None:
811 parent = wx.GetApp().GetTopWindow()
812
813 #------------------------------------------------------------
814 def show_pdf(bill):
815 if bill is None:
816 return False
817
818 # find invoice
819 invoice = bill.invoice
820 if invoice is not None:
821 success, msg = invoice.parts[-1].display_via_mime()
822 if not success:
823 gmGuiHelpers.gm_show_error(aMessage = msg, aTitle = _('Displaying invoice'))
824 return False
825
826 # create it ?
827 create_it = gmGuiHelpers.gm_show_question (
828 title = _('Displaying invoice'),
829 question = _(
830 'Cannot find an existing\n'
831 'invoice PDF for this bill.\n'
832 '\n'
833 'Do you want to create one ?'
834 ),
835 )
836 if not create_it:
837 return False
838
839 # prepare invoicing
840 if not bill.set_missing_address_from_default():
841 gmGuiHelpers.gm_show_warning (
842 aTitle = _('Creating invoice'),
843 aMessage = _(
844 'There is no pre-configured billing address.\n'
845 '\n'
846 'Select the address you want to send the bill to.'
847 )
848 )
849 edit_bill(parent = parent, bill = bill, single_entry = True)
850 if bill['pk_receiver_address'] is None:
851 return False
852 if bill['close_date'] is None:
853 bill['close_date'] = gmDateTime.pydt_now_here()
854 bill.save()
855
856 return create_invoice_from_bill(parent = parent, bill = bill, print_it = True, keep_a_copy = True)
857 #------------------------------------------------------------
858 def edit(bill):
859 return edit_bill(parent = parent, bill = bill, single_entry = True)
860 #------------------------------------------------------------
861 def delete(bill):
862 return delete_bill(parent = parent, bill = bill)
863 #------------------------------------------------------------
864 def remove_items(bill):
865 return remove_items_from_bill(parent = parent, bill = bill)
866 #------------------------------------------------------------
867 def get_tooltip(item):
868 if item is None:
869 return None
870 return item.format()
871 #------------------------------------------------------------
872 def refresh(lctrl):
873 if patient is None:
874 bills = gmBilling.get_bills()
875 else:
876 bills = gmBilling.get_bills(pk_patient = patient.ID)
877 items = []
878 for b in bills:
879 if b['close_date'] is None:
880 close_date = _('<open>')
881 else:
882 close_date = gmDateTime.pydt_strftime(b['close_date'], '%Y %b %d')
883 if b['total_amount'] is None:
884 amount = _('no items on bill')
885 else:
886 amount = gmTools.bool2subst (
887 b['apply_vat'],
888 _('%(currency)s%(total_amount_with_vat)s (with %(percent_vat)s%% VAT)') % b,
889 '%(currency)s%(total_amount)s' % b,
890 _('without VAT: %(currency)s%(total_amount)s / with %(percent_vat)s%% VAT: %(currency)s%(total_amount_with_vat)s') % b
891 )
892 items.append ([
893 close_date,
894 b['invoice_id'],
895 amount,
896 gmTools.coalesce(b['comment'], '')
897 ])
898 lctrl.set_string_items(items)
899 lctrl.set_data(bills)
900 #------------------------------------------------------------
901 return gmListWidgets.get_choices_from_list (
902 parent = parent,
903 caption = _('Showing bills.'),
904 columns = [_('Close date'), _('Invoice ID'), _('Value'), _('Comment')],
905 single_selection = True,
906 edit_callback = edit,
907 delete_callback = delete,
908 refresh_callback = refresh,
909 middle_extra_button = (
910 'PDF',
911 _('Create if necessary, and show the corresponding invoice PDF'),
912 show_pdf
913 ),
914 right_extra_button = (
915 _('Unbill'),
916 _('Select and remove items from a bill.'),
917 remove_items
918 ),
919 list_tooltip_callback = get_tooltip
920 )
921
922 #----------------------------------------------------------------
923 from Gnumed.wxGladeWidgets import wxgBillEAPnl
924
926
928
929 try:
930 data = kwargs['bill']
931 del kwargs['bill']
932 except KeyError:
933 data = None
934
935 wxgBillEAPnl.wxgBillEAPnl.__init__(self, *args, **kwargs)
936 gmEditArea.cGenericEditAreaMixin.__init__(self)
937
938 self.mode = 'new'
939 self.data = data
940 if data is not None:
941 self.mode = 'edit'
942
943 self._3state2bool = {
944 wx.CHK_UNCHECKED: False,
945 wx.CHK_CHECKED: True,
946 wx.CHK_UNDETERMINED: None
947 }
948 self.bool_to_3state = {
949 False: wx.CHK_UNCHECKED,
950 True: wx.CHK_CHECKED,
951 None: wx.CHK_UNDETERMINED
952 }
953
954 # self.__init_ui()
955 #----------------------------------------------------------------
956 # def __init_ui(self):
957 #----------------------------------------------------------------
958 # generic Edit Area mixin API
959 #----------------------------------------------------------------
961 validity = True
962
963 # flag but do not count as wrong
964 if not self._PRW_close_date.is_valid_timestamp(empty_is_valid = False):
965 self._PRW_close_date.SetFocus()
966
967 # flag but do not count as wrong
968 if self._CHBOX_vat_applies.ThreeStateValue == wx.CHK_UNDETERMINED:
969 self._CHBOX_vat_applies.SetFocus()
970 self._CHBOX_vat_applies.SetBackgroundColour('yellow')
971
972 return validity
973 #----------------------------------------------------------------
977 #----------------------------------------------------------------
979 self.data['close_date'] = self._PRW_close_date.GetData()
980 self.data['apply_vat'] = self._3state2bool[self._CHBOX_vat_applies.ThreeStateValue]
981 self.data['comment'] = self._TCTRL_comment.GetValue()
982 self.data.save()
983 return True
984 #----------------------------------------------------------------
987 #----------------------------------------------------------------
990 #----------------------------------------------------------------
992 self._TCTRL_invoice_id.SetValue(self.data['invoice_id'])
993 self._PRW_close_date.SetText(data = self.data['close_date'])
994
995 self.data.set_missing_address_from_default()
996 if self.data['pk_receiver_address'] is None:
997 self._TCTRL_address.SetValue('')
998 else:
999 adr = self.data.address
1000 self._TCTRL_address.SetValue(adr.format(single_line = True, show_type = False))
1001
1002 self._TCTRL_value.SetValue('%(currency)s%(total_amount)s' % self.data)
1003 self._CHBOX_vat_applies.ThreeStateValue = self.bool_to_3state[self.data['apply_vat']]
1004 self._CHBOX_vat_applies.SetLabel(_('&VAT applies (%s%%)') % self.data['percent_vat'])
1005 if self.data['apply_vat'] is True:
1006 tmp = '%s %%(currency)s%%(total_vat)s %s %s %%(currency)s%%(total_amount_with_vat)s' % (
1007 gmTools.u_corresponds_to,
1008 gmTools.u_arrow2right,
1009 gmTools.u_sum,
1010 )
1011 self._TCTRL_value_with_vat.SetValue(tmp % self.data)
1012 elif self.data['apply_vat'] is None:
1013 self._TCTRL_value_with_vat.SetValue('?')
1014 else:
1015 self._TCTRL_value_with_vat.SetValue('')
1016
1017 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
1018
1019 self._PRW_close_date.SetFocus()
1020 #----------------------------------------------------------------
1021 # event handling
1022 #----------------------------------------------------------------
1024 if self._CHBOX_vat_applies.ThreeStateValue == wx.CHK_CHECKED:
1025 tmp = '%s %%(currency)s%%(total_vat)s %s %s %%(currency)s%%(total_amount_with_vat)s' % (
1026 gmTools.u_corresponds_to,
1027 gmTools.u_arrow2right,
1028 gmTools.u_sum,
1029 )
1030 self._TCTRL_value_with_vat.SetValue(tmp % self.data)
1031 return
1032 if self._CHBOX_vat_applies.ThreeStateValue == wx.CHK_UNDETERMINED:
1033 self._TCTRL_value_with_vat.SetValue('?')
1034 return
1035 self._TCTRL_value_with_vat.SetValue('')
1036 #----------------------------------------------------------------
1051
1052 #================================================================
1053 # per-patient bill items related widgets
1054 #----------------------------------------------------------------
1056
1057 if bill_item is not None:
1058 if bill_item.is_in_use:
1059 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit already invoiced bill item.'), beep = True)
1060 return False
1061
1062 ea = cBillItemEAPnl(parent, -1)
1063 ea.data = bill_item
1064 ea.mode = gmTools.coalesce(bill_item, 'new', 'edit')
1065 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
1066 dlg.SetTitle(gmTools.coalesce(bill_item, _('Adding new bill item'), _('Editing bill item')))
1067 if dlg.ShowModal() == wx.ID_OK:
1068 dlg.Destroy()
1069 return True
1070 dlg.Destroy()
1071 return False
1072 #----------------------------------------------------------------
1074
1075 if parent is None:
1076 parent = wx.GetApp().GetTopWindow()
1077 #------------------------------------------------------------
1078 def edit(item=None):
1079 return edit_bill_item(parent = parent, bill_item = item, single_entry = (item is not None))
1080 #------------------------------------------------------------
1081 def delete(item):
1082 if item.is_in_use is not None:
1083 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete already invoiced bill items.'), beep = True)
1084 return False
1085 gmBilling.delete_bill_item(pk_bill_item = item['pk_bill_item'])
1086 return True
1087 #------------------------------------------------------------
1088 def get_tooltip(item):
1089 if item is None:
1090 return None
1091 return item.format()
1092 #------------------------------------------------------------
1093 def refresh(lctrl):
1094 b_items = gmBilling.get_bill_items(pk_patient = pk_patient)
1095 items = [ [
1096 gmDateTime.pydt_strftime(b['date_to_bill'], '%Y %b %d', accuracy = gmDateTime.acc_days),
1097 b['unit_count'],
1098 '%s: %s%s' % (b['billable_code'], b['billable_description'], gmTools.coalesce(b['item_detail'], '', ' - %s')),
1099 b['currency'],
1100 '%s (%s %s %s%s%s)' % (
1101 b['total_amount'],
1102 b['unit_count'],
1103 gmTools.u_multiply,
1104 b['net_amount_per_unit'],
1105 gmTools.u_multiply,
1106 b['amount_multiplier']
1107 ),
1108 '%s (%s%%)' % (
1109 b['vat'],
1110 b['vat_multiplier'] * 100
1111 ),
1112 '%s (%s)' % (b['catalog_short'], b['catalog_version']),
1113 b['pk_bill_item']
1114 ] for b in b_items ]
1115 lctrl.set_string_items(items)
1116 lctrl.set_data(b_items)
1117 #------------------------------------------------------------
1118 gmListWidgets.get_choices_from_list (
1119 parent = parent,
1120 #msg = msg,
1121 caption = _('Showing bill items.'),
1122 columns = [_('Date'), _('Count'), _('Description'), _('$__replace_by_your_currency_symbol')[:-len('__replace_by_your_currency_symbol')], _('Value'), _('VAT'), _('Catalog'), '#'],
1123 single_selection = True,
1124 new_callback = edit,
1125 edit_callback = edit,
1126 delete_callback = delete,
1127 refresh_callback = refresh,
1128 list_tooltip_callback = get_tooltip
1129 )
1130
1131 #------------------------------------------------------------
1133 """A list for managing a patient's bill items.
1134
1135 Does NOT act on/listen to the current patient.
1136 """
1138
1139 try:
1140 self.__identity = kwargs['identity']
1141 del kwargs['identity']
1142 except KeyError:
1143 self.__identity = None
1144
1145 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
1146
1147 self.refresh_callback = self.refresh
1148 self.new_callback = self._add_item
1149 self.edit_callback = self._edit_item
1150 self.delete_callback = self._del_item
1151
1152 self.__show_non_invoiced_only = True
1153
1154 self.__init_ui()
1155 self.refresh()
1156 #--------------------------------------------------------
1157 # external API
1158 #--------------------------------------------------------
1160 if self.__identity is None:
1161 self._LCTRL_items.set_string_items()
1162 return
1163
1164 b_items = gmBilling.get_bill_items(pk_patient = self.__identity.ID, non_invoiced_only = self.__show_non_invoiced_only)
1165 items = [ [
1166 gmDateTime.pydt_strftime(b['date_to_bill'], '%Y %b %d', accuracy = gmDateTime.acc_days),
1167 b['unit_count'],
1168 '%s: %s%s' % (b['billable_code'], b['billable_description'], gmTools.coalesce(b['item_detail'], '', ' - %s')),
1169 b['currency'],
1170 b['total_amount'],
1171 '%s (%s%%)' % (
1172 b['vat'],
1173 b['vat_multiplier'] * 100
1174 ),
1175 '%s (%s)' % (b['catalog_short'], b['catalog_version']),
1176 '%s %s %s %s %s' % (
1177 b['unit_count'],
1178 gmTools.u_multiply,
1179 b['net_amount_per_unit'],
1180 gmTools.u_multiply,
1181 b['amount_multiplier']
1182 ),
1183 gmTools.coalesce(b['pk_bill'], gmTools.u_diameter),
1184 b['pk_encounter_to_bill'],
1185 b['pk_bill_item']
1186 ] for b in b_items ]
1187
1188 self._LCTRL_items.set_string_items(items = items)
1189 self._LCTRL_items.set_column_widths()
1190 self._LCTRL_items.set_data(data = b_items)
1191 #--------------------------------------------------------
1192 # internal helpers
1193 #--------------------------------------------------------
1195 self._LCTRL_items.set_columns(columns = [
1196 _('Charge date'),
1197 _('Count'),
1198 _('Description'),
1199 _('$__replace_by_your_currency_symbol')[:-len('__replace_by_your_currency_symbol')],
1200 _('Value'),
1201 _('VAT'),
1202 _('Catalog'),
1203 _('Count %s Value %s Factor') % (gmTools.u_multiply, gmTools.u_multiply),
1204 _('Invoice'),
1205 _('Encounter'),
1206 '#'
1207 ])
1208 self._LCTRL_items.item_tooltip_callback = self._get_item_tooltip
1209 # self.left_extra_button = (
1210 # _('Select pending'),
1211 # _('Select non-invoiced (pending) items.'),
1212 # self._select_pending_items
1213 # )
1214 self.left_extra_button = (
1215 _('Invoice selected items'),
1216 _('Create invoice from selected items.'),
1217 self._invoice_selected_items
1218 )
1219 self.middle_extra_button = (
1220 _('Bills'),
1221 _('Browse bills of this patient.'),
1222 self._browse_bills
1223 )
1224 self.right_extra_button = (
1225 _('Billables'),
1226 _('Browse list of billables.'),
1227 self._browse_billables
1228 )
1229 #--------------------------------------------------------
1232 #--------------------------------------------------------
1235 #--------------------------------------------------------
1237 if item['pk_bill'] is not None:
1238 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete already invoiced bill items.'), beep = True)
1239 return False
1240 go_ahead = gmGuiHelpers.gm_show_question (
1241 _( 'Do you really want to delete this\n'
1242 'bill item from the patient ?'),
1243 _('Deleting bill item')
1244 )
1245 if not go_ahead:
1246 return False
1247 gmBilling.delete_bill_item(pk_bill_item = item['pk_bill_item'])
1248 return True
1249 #--------------------------------------------------------
1254 #--------------------------------------------------------
1257 #--------------------------------------------------------
1259 bill_items = self._LCTRL_items.get_selected_item_data()
1260 bill = create_bill_from_items(bill_items)
1261 if bill is None:
1262 return
1263 if bill['pk_receiver_address'] is None:
1264 gmGuiHelpers.gm_show_error (
1265 aMessage = _(
1266 'Cannot create invoice.\n'
1267 '\n'
1268 'No receiver address selected.'
1269 ),
1270 aTitle = _('Creating invoice')
1271 )
1272 return
1273 if bill['close_date'] is None:
1274 bill['close_date'] = gmDateTime.pydt_now_here()
1275 bill.save()
1276 create_invoice_from_bill(parent = self, bill = bill, print_it = True, keep_a_copy = True)
1277 #--------------------------------------------------------
1281 #--------------------------------------------------------
1284 #--------------------------------------------------------
1285 # properties
1286 #--------------------------------------------------------
1289
1293
1294 identity = property(_get_identity, _set_identity)
1295 #--------------------------------------------------------
1298
1302
1303 show_non_invoiced_only = property(_get_show_non_invoiced_only, _set_show_non_invoiced_only)
1304
1305 #------------------------------------------------------------
1306 from Gnumed.wxGladeWidgets import wxgBillItemEAPnl
1307
1309
1311
1312 try:
1313 data = kwargs['bill_item']
1314 del kwargs['bill_item']
1315 except KeyError:
1316 data = None
1317
1318 wxgBillItemEAPnl.wxgBillItemEAPnl.__init__(self, *args, **kwargs)
1319 gmEditArea.cGenericEditAreaMixin.__init__(self)
1320
1321 self.mode = 'new'
1322 self.data = data
1323 if data is not None:
1324 self.mode = 'edit'
1325
1326 self.__init_ui()
1327 #----------------------------------------------------------------
1329 self._PRW_encounter.set_context(context = 'patient', val = gmPerson.gmCurrentPatient().ID)
1330 self._PRW_billable.add_callback_on_selection(self._on_billable_selected)
1331 self._PRW_billable.add_callback_on_modified(self._on_billable_modified)
1332 #----------------------------------------------------------------
1333 # generic Edit Area mixin API
1334 #----------------------------------------------------------------
1336
1337 validity = True
1338
1339 if self._TCTRL_factor.GetValue().strip() == '':
1340 validity = False
1341 self.display_tctrl_as_valid(tctrl = self._TCTRL_factor, valid = False)
1342 self._TCTRL_factor.SetFocus()
1343 else:
1344 converted, factor = gmTools.input2decimal(self._TCTRL_factor.GetValue())
1345 if not converted:
1346 validity = False
1347 self.display_tctrl_as_valid(tctrl = self._TCTRL_factor, valid = False)
1348 self._TCTRL_factor.SetFocus()
1349 else:
1350 self.display_tctrl_as_valid(tctrl = self._TCTRL_factor, valid = True)
1351
1352 if self._TCTRL_amount.GetValue().strip() == '':
1353 validity = False
1354 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
1355 self._TCTRL_amount.SetFocus()
1356 else:
1357 converted, factor = gmTools.input2decimal(self._TCTRL_amount.GetValue())
1358 if not converted:
1359 validity = False
1360 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
1361 self._TCTRL_amount.SetFocus()
1362 else:
1363 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
1364
1365 if self._TCTRL_count.GetValue().strip() == '':
1366 validity = False
1367 self.display_tctrl_as_valid(tctrl = self._TCTRL_count, valid = False)
1368 self._TCTRL_count.SetFocus()
1369 else:
1370 converted, factor = gmTools.input2decimal(self._TCTRL_count.GetValue())
1371 if not converted:
1372 validity = False
1373 self.display_tctrl_as_valid(tctrl = self._TCTRL_count, valid = False)
1374 self._TCTRL_count.SetFocus()
1375 else:
1376 self.display_tctrl_as_valid(tctrl = self._TCTRL_count, valid = True)
1377
1378 if self._PRW_date.is_valid_timestamp(empty_is_valid = True):
1379 self._PRW_date.display_as_valid(True)
1380 else:
1381 validity = False
1382 self._PRW_date.display_as_valid(False)
1383 self._PRW_date.SetFocus()
1384
1385 if self._PRW_encounter.GetData() is None:
1386 validity = False
1387 self._PRW_encounter.display_as_valid(False)
1388 self._PRW_encounter.SetFocus()
1389 else:
1390 self._PRW_encounter.display_as_valid(True)
1391
1392 if self._PRW_billable.GetData() is None:
1393 validity = False
1394 self._PRW_billable.display_as_valid(False)
1395 self._PRW_billable.SetFocus()
1396 else:
1397 self._PRW_billable.display_as_valid(True)
1398
1399 return validity
1400 #----------------------------------------------------------------
1402 data = gmBilling.create_bill_item (
1403 pk_encounter = self._PRW_encounter.GetData(),
1404 pk_billable = self._PRW_billable.GetData(),
1405 pk_staff = gmStaff.gmCurrentProvider()['pk_staff'] # should be settable !
1406 )
1407 data['raw_date_to_bill'] = self._PRW_date.GetData()
1408 converted, data['unit_count'] = gmTools.input2decimal(self._TCTRL_count.GetValue())
1409 converted, data['net_amount_per_unit'] = gmTools.input2decimal(self._TCTRL_amount.GetValue())
1410 converted, data['amount_multiplier'] = gmTools.input2decimal(self._TCTRL_factor.GetValue())
1411 data['item_detail'] = self._TCTRL_comment.GetValue().strip()
1412 data.save()
1413
1414 self.data = data
1415 return True
1416 #----------------------------------------------------------------
1418 self.data['pk_encounter_to_bill'] = self._PRW_encounter.GetData()
1419 self.data['raw_date_to_bill'] = self._PRW_date.GetData()
1420 converted, self.data['unit_count'] = gmTools.input2decimal(self._TCTRL_count.GetValue())
1421 converted, self.data['net_amount_per_unit'] = gmTools.input2decimal(self._TCTRL_amount.GetValue())
1422 converted, self.data['amount_multiplier'] = gmTools.input2decimal(self._TCTRL_factor.GetValue())
1423 self.data['item_detail'] = self._TCTRL_comment.GetValue().strip()
1424 return self.data.save()
1425 #----------------------------------------------------------------
1427 self._PRW_billable.SetText()
1428 self._PRW_encounter.set_from_instance(gmPerson.gmCurrentPatient().emr.active_encounter)
1429 self._PRW_date.SetData()
1430 self._TCTRL_count.SetValue('1')
1431 self._TCTRL_amount.SetValue('')
1432 self._LBL_currency.SetLabel(gmTools.u_euro)
1433 self._TCTRL_factor.SetValue('1')
1434 self._TCTRL_comment.SetValue('')
1435
1436 self._PRW_billable.Enable()
1437 self._PRW_billable.SetFocus()
1438 #----------------------------------------------------------------
1440 self._PRW_billable.SetText()
1441 self._TCTRL_count.SetValue('1')
1442 self._TCTRL_amount.SetValue('')
1443 self._TCTRL_comment.SetValue('')
1444
1445 self._PRW_billable.Enable()
1446 self._PRW_billable.SetFocus()
1447 #----------------------------------------------------------------
1449 self._PRW_billable.set_from_pk(self.data['pk_billable'])
1450 self._PRW_encounter.SetData(self.data['pk_encounter_to_bill'])
1451 self._PRW_date.SetData(data = self.data['raw_date_to_bill'])
1452 self._TCTRL_count.SetValue('%s' % self.data['unit_count'])
1453 self._TCTRL_amount.SetValue('%s' % self.data['net_amount_per_unit'])
1454 self._LBL_currency.SetLabel(self.data['currency'])
1455 self._TCTRL_factor.SetValue('%s' % self.data['amount_multiplier'])
1456 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['item_detail'], ''))
1457
1458 self._PRW_billable.Disable()
1459 self._PRW_date.SetFocus()
1460 #----------------------------------------------------------------
1462 if item is None:
1463 return
1464 if self._TCTRL_amount.GetValue().strip() != '':
1465 return
1466 val = '%s' % self._PRW_billable.GetData(as_instance = True)['raw_amount']
1467 wx.CallAfter(self._TCTRL_amount.SetValue, val)
1468 #----------------------------------------------------------------
1472
1473 #============================================================
1474 # a plugin for billing
1475 #------------------------------------------------------------
1476 from Gnumed.wxGladeWidgets import wxgBillingPluginPnl
1477
1478 -class cBillingPluginPnl(wxgBillingPluginPnl.wxgBillingPluginPnl, gmRegetMixin.cRegetOnPaintMixin):
1480
1481 wxgBillingPluginPnl.wxgBillingPluginPnl.__init__(self, *args, **kwargs)
1482 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1483 self.__register_interests()
1484 #-----------------------------------------------------
1486 self._PNL_bill_items.identity = None
1487 self._CHBOX_show_non_invoiced_only.SetValue(1)
1488 self._PRW_billable.SetText('', None)
1489 self._TCTRL_factor.SetValue('1.0')
1490 self._TCTRL_factor.Disable()
1491 self._TCTRL_details.SetValue('')
1492 self._TCTRL_details.Disable()
1493 #-----------------------------------------------------
1494 # event handling
1495 #-----------------------------------------------------
1497 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
1498 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
1499
1500 gmDispatcher.connect(signal = 'bill.bill_item_mod_db', receiver = self._on_bill_item_modified)
1501
1502 self._PRW_billable.add_callback_on_selection(self._on_billable_selected_in_prw)
1503 #-----------------------------------------------------
1506 #-----------------------------------------------------
1509 #-----------------------------------------------------
1512 #-----------------------------------------------------
1515 #--------------------------------------------------------
1546 #--------------------------------------------------------
1548 if billable is None:
1549 self._TCTRL_factor.Disable()
1550 self._TCTRL_details.Disable()
1551 self._BTN_insert_item.Disable()
1552 else:
1553 self._TCTRL_factor.Enable()
1554 self._TCTRL_details.Enable()
1555 self._BTN_insert_item.Enable()
1556 #-----------------------------------------------------
1557 # reget-on-paint mixin API
1558 #-----------------------------------------------------
1562 #============================================================
1563 # main
1564 #------------------------------------------------------------
1565 if __name__ == '__main__':
1566
1567 if len(sys.argv) < 2:
1568 sys.exit()
1569
1570 if sys.argv[1] != 'test':
1571 sys.exit()
1572
1573 from Gnumed.pycommon import gmI18N
1574 gmI18N.activate_locale()
1575 gmI18N.install_domain(domain = 'gnumed')
1576
1577 #----------------------------------------
1578 app = wx.PyWidgetTester(size = (600, 600))
1579 #app.SetWidget(cXxxPhraseWheel, -1)
1580 app.MainLoop()
1581
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri Jan 25 02:55:27 2019 | http://epydoc.sourceforge.net |