1 """GNUmed billing handling widgets.
2 """
3
4 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL v2 or later"
6
7 import logging
8 import sys
9
10
11 import wx
12
13
14 if __name__ == '__main__':
15 sys.path.insert(0, '../../')
16 from Gnumed.pycommon import gmTools
17 from Gnumed.pycommon import gmDateTime
18 from Gnumed.pycommon import gmMatchProvider
19 from Gnumed.pycommon import gmDispatcher
20 from Gnumed.pycommon import gmPG2
21 from Gnumed.pycommon import gmCfg
22 from Gnumed.pycommon import gmPrinting
23 from Gnumed.pycommon import gmNetworkTools
24
25 from Gnumed.business import gmBilling
26 from Gnumed.business import gmPerson
27 from Gnumed.business import gmStaff
28 from Gnumed.business import gmDocuments
29 from Gnumed.business import gmSurgery
30 from Gnumed.business import gmForms
31 from Gnumed.business import gmDemographicRecord
32
33 from Gnumed.wxpython import gmListWidgets
34 from Gnumed.wxpython import gmRegetMixin
35 from Gnumed.wxpython import gmPhraseWheel
36 from Gnumed.wxpython import gmGuiHelpers
37 from Gnumed.wxpython import gmEditArea
38 from Gnumed.wxpython import gmPersonContactWidgets
39 from Gnumed.wxpython import gmMacro
40 from Gnumed.wxpython import gmFormWidgets
41 from Gnumed.wxpython import gmDocumentWidgets
42 from Gnumed.wxpython import gmDataPackWidgets
43
44
45 _log = logging.getLogger('gm.ui')
46
47
49
50 if parent is None:
51 parent = wx.GetApp().GetTopWindow()
52
53
54
55
56 def delete(billable):
57 if billable.is_in_use:
58 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this billable item. It is in use.'), beep = True)
59 return False
60 return gmBilling.delete_billable(pk_billable = billable['pk_billable'])
61
62 def get_tooltip(item):
63 if item is None:
64 return None
65 return item.format()
66
67 def refresh(lctrl):
68 billables = gmBilling.get_billables()
69 items = [ [
70 b['billable_code'],
71 b['billable_description'],
72 u'%s %s' % (b['raw_amount'], b['currency']),
73 u'%s (%s)' % (b['catalog_short'], b['catalog_version']),
74 gmTools.coalesce(b['comment'], u''),
75 b['pk_billable']
76 ] for b in billables ]
77 lctrl.set_string_items(items)
78 lctrl.set_data(billables)
79
80 def manage_data_packs(billable):
81 gmDataPackWidgets.manage_data_packs(parent = parent)
82 return True
83
84 def browse_catalogs(billable):
85 dbcfg = gmCfg.cCfgSQL()
86 url = dbcfg.get2 (
87 option = 'external.urls.schedules_of_fees',
88 workplace = gmSurgery.gmCurrentPractice().active_workplace,
89 bias = 'user',
90 default = u'http://www.e-bis.de/goae/defaultFrame.htm'
91 )
92 gmNetworkTools.open_url_in_browser(url = url)
93 return False
94
95 msg = _('\nThese are the items for billing registered with GNUmed.\n')
96
97 gmListWidgets.get_choices_from_list (
98 parent = parent,
99 msg = msg,
100 caption = _('Showing billable items.'),
101 columns = [_('Code'), _('Description'), _('Value'), _('Catalog'), _('Comment'), u'#'],
102 single_selection = True,
103
104
105 delete_callback = delete,
106 refresh_callback = refresh,
107 middle_extra_button = (
108 _('Data packs'),
109 _('Browse and install billing catalog (schedule of fees) data packs'),
110 manage_data_packs
111 ),
112 right_extra_button = (
113 _('Catalogs (WWW)'),
114 _('Browse billing catalogs (schedules of fees) on the web'),
115 browse_catalogs
116 ),
117 list_tooltip_callback = get_tooltip
118 )
119
120
122
124 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
125 query = u"""
126 SELECT -- DISTINCT ON (label)
127 r_vb.pk_billable
128 AS data,
129 r_vb.billable_code || ': ' || r_vb.billable_description || ' (' || r_vb.catalog_short || ' - ' || r_vb.catalog_version || ')'
130 AS list_label,
131 r_vb.billable_code || ' (' || r_vb.catalog_short || ' - ' || r_vb.catalog_version || ')'
132 AS field_label
133 FROM
134 ref.v_billables r_vb
135 WHERE
136 r_vb.active
137 AND (
138 r_vb.billable_code %(fragment_condition)s
139 OR
140 r_vb.billable_description %(fragment_condition)s
141 )
142 ORDER BY list_label
143 LIMIT 20
144 """
145 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
146 mp.setThresholds(1, 2, 4)
147 self.matcher = mp
148
151
157
159 val = u'%s (%s - %s)' % (
160 instance['billable_code'],
161 instance['catalog_short'],
162 instance['catalog_version']
163 )
164 self.SetText(value = val, data = instance['pk_billable'])
165
168
169
170
171
203
205
206 dbcfg = gmCfg.cCfgSQL()
207 if with_vat:
208 option = u'form_templates.invoice_with_vat'
209 else:
210 option = u'form_templates.invoice_no_vat'
211
212 template = dbcfg.get2 (
213 option = option,
214 workplace = gmSurgery.gmCurrentPractice().active_workplace,
215 bias = 'user'
216 )
217
218 if template is None:
219 template = configure_invoice_template(parent = parent, with_vat = with_vat)
220 if template is None:
221 gmGuiHelpers.gm_show_error (
222 aMessage = _('There is no invoice template configured.'),
223 aTitle = _('Getting invoice template')
224 )
225 return None
226 else:
227 try:
228 name, ver = template.split(u' - ')
229 except:
230 _log.exception('problem splitting invoice template name [%s]', template)
231 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading invoice template.'), beep = True)
232 return None
233 template = gmForms.get_form_template(name_long = name, external_version = ver)
234 if template is None:
235 gmGuiHelpers.gm_show_error (
236 aMessage = _('Cannot load invoice template [%s - %s]') % (name, ver),
237 aTitle = _('Getting invoice template')
238 )
239 return None
240
241 return template
242
243
244
245
246 -def edit_bill(parent=None, bill=None, single_entry=False):
262
264
265 if len(bill_items) == 0:
266 return None
267
268 item = bill_items[0]
269 currency = item['currency']
270 vat = item['vat_multiplier']
271 pat = item['pk_patient']
272
273
274 has_errors = False
275 for item in bill_items:
276 if (item['currency'] != currency) or (
277 item['vat_multiplier'] != vat) or (
278 item['pk_patient'] != pat
279 ):
280 msg = _(
281 'All items to be included with a bill must\n'
282 'coincide on currency, VAT, and patient.\n'
283 '\n'
284 'This item does not:\n'
285 '\n'
286 '%s\n'
287 ) % item.format()
288 has_errors = True
289
290 if item['pk_bill'] is not None:
291 msg = _(
292 'This item is already invoiced:\n'
293 '\n'
294 '%s\n'
295 '\n'
296 'Cannot put it on a second bill.'
297 ) % item.format()
298 has_errors = True
299
300 if has_errors:
301 gmGuiHelpers.gm_show_warning(aTitle = _('Checking invoice items'), aMessage = msg)
302 return None
303
304
305 bill = gmBilling.create_bill(invoice_id = gmBilling.get_invoice_id(pk_patient = pat))
306 _log.info('created bill [%s]', bill['invoice_id'])
307 bill.add_items(items = bill_items)
308 bill.set_missing_address_from_default()
309
310 return bill
311
313
314 if None in [ bill['close_date'], bill['pk_receiver_address'] ]:
315 edit_bill(parent = parent, bill = bill, single_entry = True)
316
317 if bill['close_date'] is None:
318 _log.error('cannot create invoice from bill, bill not closed')
319 gmGuiHelpers.gm_show_warning (
320 aTitle = _('Creating invoice'),
321 aMessage = _(
322 'Cannot create invoice from bill.\n'
323 '\n'
324 'The bill is not closed.'
325 )
326 )
327 return False
328
329 if bill['pk_receiver_address'] is None:
330 _log.error('cannot create invoice from bill, lacking receiver address')
331 gmGuiHelpers.gm_show_warning (
332 aTitle = _('Creating invoice'),
333 aMessage = _(
334 'Cannot create invoice from bill.\n'
335 '\n'
336 'There is no receiver address.'
337 )
338 )
339 return False
340
341
342 template = get_invoice_template(parent = parent, with_vat = bill['apply_vat'])
343 if template is None:
344 gmGuiHelpers.gm_show_warning (
345 aTitle = _('Creating invoice'),
346 aMessage = _(
347 'Cannot create invoice from bill\n'
348 'without an invoice template.'
349 )
350 )
351 return False
352
353
354 try:
355 invoice = template.instantiate()
356 except KeyError:
357 _log.exception('cannot instantiate invoice template [%s]', template)
358 gmGuiHelpers.gm_show_error (
359 aMessage = _('Invalid invoice template [%s - %s (%s)]') % (name, ver, template['engine']),
360 aTitle = _('Printing medication list')
361 )
362 return False
363
364 ph = gmMacro.gmPlaceholderHandler()
365
366 ph.set_cache_value('bill', bill)
367 invoice.substitute_placeholders(data_source = ph)
368 ph.unset_cache_value('bill')
369 pdf_name = invoice.generate_output()
370 if pdf_name is None:
371 gmGuiHelpers.gm_show_error (
372 aMessage = _('Error generating invoice PDF.'),
373 aTitle = _('Creating invoice')
374 )
375 return False
376
377
378 if keep_a_copy:
379 files2import = []
380 files2import.extend(invoice.final_output_filenames)
381 files2import.extend(invoice.re_editable_filenames)
382 doc = gmDocumentWidgets.save_files_as_new_document (
383 parent = parent,
384 filenames = files2import,
385 document_type = template['instance_type'],
386 review_as_normal = True,
387 reference = bill['invoice_id']
388 )
389 bill['pk_doc'] = doc['pk_doc']
390 bill.save()
391
392 if not print_it:
393 return True
394
395
396 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'invoice')
397 if not printed:
398 gmGuiHelpers.gm_show_error (
399 aMessage = _('Error printing the invoice.'),
400 aTitle = _('Printing invoice')
401 )
402 return True
403
404 return True
405
406
408
409 if parent is None:
410 parent = wx.GetApp().GetTopWindow()
411
412 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
413 parent, -1,
414 caption = _('Deleting bill'),
415 question = _(
416 'When deleting the bill [%s]\n'
417 'do you want to keep its items (effectively \"unbilling\" them)\n'
418 'or do you want to also delete the bill items from the patient ?\n'
419 ) % bill['invoice_id'],
420 button_defs = [
421 {'label': _('Delete + keep'), 'tooltip': _('Delete the bill but keep ("unbill") its items.'), 'default': True},
422 {'label': _('Delete all'), 'tooltip': _('Delete both the bill and its items from the patient.')}
423 ],
424 show_checkbox = True,
425 checkbox_msg = _('Also remove invoice PDF'),
426 checkbox_tooltip = _('Also remove the invoice PDF from the document archive (because it will not correspond to the bill anymore).')
427 )
428 button_pressed = dlg.ShowModal()
429 delete_invoice = dlg.checkbox_is_checked()
430 dlg.Destroy()
431
432 if button_pressed == wx.ID_CANCEL:
433 return False
434
435 if button_pressed == wx.ID_YES:
436 for item in bill.bill_items:
437 item['pk_bill'] = None
438 item.save()
439
440 if button_pressed == wx.ID_NO:
441 for item in bill.bill_items:
442 item['pk_bill'] = None
443 item.save()
444 gmBilling.delete_bill_item(pk_bill_item = item['pk_bill_item'])
445
446 if delete_invoice:
447 if bill['pk_doc'] is not None:
448 gmDocuments.delete_document (
449 document_id = bill['pk_doc'],
450 encounter_id = gmPerson.cPatient(aPK_obj = bill['pk_patient']).emr.active_encounter['pk_encounter']
451 )
452
453 return gmBilling.delete_bill(pk_bill = bill['pk_bill'])
454
455
457
458 if bill is None:
459 return False
460
461 list_data = bill.bill_items
462 if len(list_data) == 0:
463 return False
464
465 if parent is None:
466 parent = wx.GetApp().GetTopWindow()
467
468 list_items = [ [
469 gmDateTime.pydt_strftime(b['date_to_bill'], '%x', accuracy = gmDateTime.acc_days),
470 b['unit_count'],
471 u'%s: %s%s' % (b['billable_code'], b['billable_description'], gmTools.coalesce(b['item_detail'], u'', u' - %s')),
472 u'%s %s (%s %s %s%s%s)' % (
473 b['total_amount'],
474 b['currency'],
475 b['unit_count'],
476 gmTools.u_multiply,
477 b['net_amount_per_unit'],
478 gmTools.u_multiply,
479 b['amount_multiplier']
480 ),
481 u'%s %s (%s%%)' % (
482 b['vat'],
483 b['currency'],
484 b['vat_multiplier'] * 100
485 ),
486 u'%s (%s)' % (b['catalog_short'], b['catalog_version']),
487 b['pk_bill_item']
488 ] for b in list_data ]
489
490 msg = _('Select the items you want to remove from bill [%s]:\n') % bill['invoice_id']
491 items2remove = gmListWidgets.get_choices_from_list (
492 parent = parent,
493 msg = msg,
494 caption = _('Removing items from bill'),
495 columns = [_('Date'), _('Count'), _('Description'), _('Value'), _('VAT'), _('Catalog'), u'#'],
496 single_selection = False,
497 choices = list_items,
498 data = list_data
499 )
500
501 if items2remove is None:
502 return False
503
504 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
505 parent, -1,
506 caption = _('Removing items from bill'),
507 question = _(
508 '%s items selected from bill [%s]\n'
509 '\n'
510 'Do you want to only remove the selected items\n'
511 'from the bill ("unbill" them) or do you want\n'
512 'to delete them entirely from the patient ?\n'
513 '\n'
514 'Note that neither action is reversible.'
515 ) % (
516 len(items2remove),
517 bill['invoice_id']
518 ),
519 button_defs = [
520 {'label': _('"Unbill"'), 'tooltip': _('Only "unbill" items (remove from bill but do not delete from patient).'), 'default': True},
521 {'label': _('Delete'), 'tooltip': _('Completely delete items from the patient.')}
522 ],
523 show_checkbox = True,
524 checkbox_msg = _('Also remove invoice PDF'),
525 checkbox_tooltip = _('Also remove the invoice PDF from the document archive (because it will not correspond to the bill anymore).')
526 )
527 button_pressed = dlg.ShowModal()
528 delete_invoice = dlg.checkbox_is_checked()
529 dlg.Destroy()
530
531 if button_pressed == wx.ID_CANCEL:
532 return False
533
534
535
536 pk_patient = bill['pk_patient']
537
538 for item in items2remove:
539 item['pk_bill'] = None
540 item.save()
541 if button_pressed == wx.ID_NO:
542 gmBilling.delete_bill_item(pk_bill_item = item['pk_bill_item'])
543
544 if delete_invoice:
545 if bill['pk_doc'] is not None:
546 gmDocuments.delete_document (
547 document_id = bill['pk_doc'],
548 encounter_id = gmPerson.cPatient(aPK_obj = pk_patient).emr.active_encounter['pk_encounter']
549 )
550
551
552 if len(bill.bill_items) == 0:
553 gmBilling.delete_bill(pk_bill = bill['pk_bill'])
554
555 return True
556
558
559 if parent is None:
560 parent = wx.GetApp().GetTopWindow()
561
562
563 def show_pdf(bill):
564 if bill is None:
565 return False
566
567
568 invoice = bill.invoice
569 if invoice is not None:
570 success, msg = invoice.parts[-1].display_via_mime()
571 if not success:
572 gmGuiHelpers.gm_show_error(aMessage = msg, aTitle = _('Displaying invoice'))
573 return False
574
575
576 create_it = gmGuiHelpers.gm_show_question (
577 title = _('Displaying invoice'),
578 question = _(
579 'Cannot find an existing\n'
580 'invoice PDF for this bill.\n'
581 '\n'
582 'Do you want to create one ?'
583 ),
584 )
585 if not create_it:
586 return False
587
588
589 if not bill.set_missing_address_from_default():
590 gmGuiHelpers.gm_show_warning (
591 aTitle = _('Creating invoice'),
592 aMessage = _(
593 'There is no pre-configured billing address.\n'
594 '\n'
595 'Select the address you want to send the bill to.'
596 )
597 )
598 edit_bill(parent = parent, bill = bill, single_entry = True)
599 if bill['pk_receiver_address'] is None:
600 return False
601 if bill['close_date'] is None:
602 bill['close_date'] = gmDateTime.pydt_now_here()
603 bill.save()
604
605 return create_invoice_from_bill(parent = parent, bill = bill, print_it = True, keep_a_copy = True)
606
607 def edit(bill):
608 return edit_bill(parent = parent, bill = bill, single_entry = True)
609
610 def delete(bill):
611 return delete_bill(parent = parent, bill = bill)
612
613 def remove_items(bill):
614 return remove_items_from_bill(parent = parent, bill = bill)
615
616 def get_tooltip(item):
617 if item is None:
618 return None
619 return item.format()
620
621 def refresh(lctrl):
622 if patient is None:
623 bills = gmBilling.get_bills()
624 else:
625 bills = gmBilling.get_bills(pk_patient = patient.ID)
626 items = []
627 for b in bills:
628 if b['close_date'] is None:
629 close_date = _('<open>')
630 else:
631 close_date = gmDateTime.pydt_strftime(b['close_date'], '%Y %b %d')
632 if b['total_amount'] is None:
633 amount = _('no items on bill')
634 else:
635 amount = gmTools.bool2subst (
636 b['apply_vat'],
637 _('%s %s (with %s%% VAT)') % (b['total_amount_with_vat'], b['currency'], b['percent_vat']),
638 u'%s %s' % (b['total_amount'], b['currency'])
639 )
640 items.append ([
641 close_date,
642 b['invoice_id'],
643 amount
644 ])
645 lctrl.set_string_items(items)
646 lctrl.set_data(bills)
647
648 return gmListWidgets.get_choices_from_list (
649 parent = parent,
650 caption = _('Showing bills.'),
651 columns = [_('Close date'), _('Invoice ID'), _('Value')],
652 single_selection = True,
653 edit_callback = edit,
654 delete_callback = delete,
655 refresh_callback = refresh,
656 middle_extra_button = (
657 u'PDF',
658 _('Create if necessary, and show the corresponding invoice PDF'),
659 show_pdf
660 ),
661 right_extra_button = (
662 _('Unbill'),
663 _('Select and remove items from a bill.'),
664 remove_items
665 ),
666 list_tooltip_callback = get_tooltip
667 )
668
669
670 from Gnumed.wxGladeWidgets import wxgBillEAPnl
671
672 -class cBillEAPnl(wxgBillEAPnl.wxgBillEAPnl, gmEditArea.cGenericEditAreaMixin):
673
689
690
691
692
693
694
695
697 validity = True
698
699
700 if not self._PRW_close_date.is_valid_timestamp(allow_empty = False):
701 self._PRW_close_date.SetFocus()
702
703 return validity
704
708
710 self.data['close_date'] = self._PRW_close_date.GetData()
711 self.data['apply_vat'] = self._CHBOX_vat_applies.GetValue()
712 self.data.save()
713 return True
714
717
719 self._refresh_as_new()
720
752
753
754
768
783
784
785
786
788
789 if bill_item is not None:
790 if bill_item.is_in_use:
791 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit already invoiced bill item.'), beep = True)
792 return False
793
794 ea = cBillItemEAPnl(parent = parent, id = -1)
795 ea.data = bill_item
796 ea.mode = gmTools.coalesce(bill_item, 'new', 'edit')
797 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
798 dlg.SetTitle(gmTools.coalesce(bill_item, _('Adding new bill item'), _('Editing bill item')))
799 if dlg.ShowModal() == wx.ID_OK:
800 dlg.Destroy()
801 return True
802 dlg.Destroy()
803 return False
804
806
807 if parent is None:
808 parent = wx.GetApp().GetTopWindow()
809
810 def edit(item=None):
811 return edit_bill_item(parent = parent, bill_item = item, single_entry = (item is not None))
812
813 def delete(item):
814 if item.is_in_use is not None:
815 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete already invoiced bill items.'), beep = True)
816 return False
817 gmBilling.delete_bill_item(pk_bill_item = item['pk_bill_item'])
818 return True
819
820 def get_tooltip(item):
821 if item is None:
822 return None
823 return item.format()
824
825 def refresh(lctrl):
826 b_items = gmBilling.get_bill_items(pk_patient = pk_patient)
827 items = [ [
828 gmDateTime.pydt_strftime(b['date_to_bill'], '%x', accuracy = gmDateTime.acc_days),
829 b['unit_count'],
830 u'%s: %s%s' % (b['billable_code'], b['billable_description'], gmTools.coalesce(b['item_detail'], u'', u' - %s')),
831 b['currency'],
832 u'%s (%s %s %s%s%s)' % (
833 b['total_amount'],
834 b['unit_count'],
835 gmTools.u_multiply,
836 b['net_amount_per_unit'],
837 gmTools.u_multiply,
838 b['amount_multiplier']
839 ),
840 u'%s (%s%%)' % (
841 b['vat'],
842 b['vat_multiplier'] * 100
843 ),
844 u'%s (%s)' % (b['catalog_short'], b['catalog_version']),
845 b['pk_bill_item']
846 ] for b in b_items ]
847 lctrl.set_string_items(items)
848 lctrl.set_data(b_items)
849
850 gmListWidgets.get_choices_from_list (
851 parent = parent,
852
853 caption = _('Showing bill items.'),
854 columns = [_('Date'), _('Count'), _('Description'), u'%s$%s%s' % (gmTools.u_euro, gmTools.u_currency_pound, gmTools.u_kanji_yen), _('Value'), _('VAT'), _('Catalog'), u'#'],
855 single_selection = True,
856 new_callback = edit,
857 edit_callback = edit,
858 delete_callback = delete,
859 refresh_callback = refresh,
860 list_tooltip_callback = get_tooltip
861 )
862
863
865 """A list for managing a patient's bill items.
866
867 Does NOT act on/listen to the current patient.
868 """
888
889
890
891 - def refresh(self, *args, **kwargs):
892 if self.__identity is None:
893 self._LCTRL_items.set_string_items()
894 return
895
896 b_items = gmBilling.get_bill_items(pk_patient = self.__identity.ID, non_invoiced_only = self.__show_non_invoiced_only)
897 items = [ [
898 gmDateTime.pydt_strftime(b['date_to_bill'], '%x', accuracy = gmDateTime.acc_days),
899 b['unit_count'],
900 u'%s: %s%s' % (b['billable_code'], b['billable_description'], gmTools.coalesce(b['item_detail'], u'', u' - %s')),
901 b['currency'],
902 b['total_amount'],
903 u'%s (%s%%)' % (
904 b['vat'],
905 b['vat_multiplier'] * 100
906 ),
907 u'%s (%s)' % (b['catalog_short'], b['catalog_version']),
908 u'%s %s %s %s %s' % (
909 b['unit_count'],
910 gmTools.u_multiply,
911 b['net_amount_per_unit'],
912 gmTools.u_multiply,
913 b['amount_multiplier']
914 ),
915 gmTools.coalesce(b['pk_bill'], gmTools.u_diameter),
916 b['pk_encounter_to_bill'],
917 b['pk_bill_item']
918 ] for b in b_items ]
919
920 self._LCTRL_items.set_string_items(items = items)
921 self._LCTRL_items.set_column_widths()
922 self._LCTRL_items.set_data(data = b_items)
923
924
925
927 self._LCTRL_items.set_columns(columns = [
928 _('Charge date'),
929 _('Count'),
930 _('Description'),
931 u'%s$%s%s' % (gmTools.u_euro, gmTools.u_currency_pound, gmTools.u_kanji_yen),
932 _('Value'),
933 _('VAT'),
934 _('Catalog'),
935 _('Count %s Value %s Factor') % (gmTools.u_multiply, gmTools.u_multiply),
936 _('Invoice'),
937 _('Encounter'),
938 u'#'
939 ])
940 self._LCTRL_items.item_tooltip_callback = self._get_item_tooltip
941
942
943
944
945
946 self.left_extra_button = (
947 _('Invoice selected items'),
948 _('Create invoice from selected items.'),
949 self._invoice_selected_items
950 )
951 self.middle_extra_button = (
952 _('Bills'),
953 _('Browse bills of this patient.'),
954 self._browse_bills
955 )
956 self.right_extra_button = (
957 _('Billables'),
958 _('Browse list of billables.'),
959 self._browse_billables
960 )
961
963 return edit_bill_item(parent = self, bill_item = None, single_entry = False)
964
966 return edit_bill_item(parent = self, bill_item = bill_item, single_entry = True)
967
969 if item['pk_bill'] is not None:
970 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete already invoiced bill items.'), beep = True)
971 return False
972 go_ahead = gmGuiHelpers.gm_show_question (
973 _( 'Do you really want to delete this\n'
974 'bill item from the patient ?'),
975 _('Deleting bill item')
976 )
977 if not go_ahead:
978 return False
979 gmBilling.delete_bill_item(pk_bill_item = item['pk_bill_item'])
980 return True
981
986
989
1009
1013
1016
1017
1018
1020 return self.__identity
1021
1025
1026 identity = property(_get_identity, _set_identity)
1027
1029 return self.__show_non_invoiced_only
1030
1032 self.__show_non_invoiced_only = value
1033 self.refresh()
1034
1035 show_non_invoiced_only = property(_get_show_non_invoiced_only, _set_show_non_invoiced_only)
1036
1037
1038 from Gnumed.wxGladeWidgets import wxgBillItemEAPnl
1039
1040 -class cBillItemEAPnl(wxgBillItemEAPnl.wxgBillItemEAPnl, gmEditArea.cGenericEditAreaMixin):
1041
1059
1063
1064
1065
1067
1068 validity = True
1069
1070 if self._TCTRL_factor.GetValue().strip() == u'':
1071 validity = False
1072 self.display_tctrl_as_valid(tctrl = self._TCTRL_factor, valid = False)
1073 self._TCTRL_factor.SetFocus()
1074 else:
1075 converted, factor = gmTools.input2decimal(self._TCTRL_factor.GetValue())
1076 if not converted:
1077 validity = False
1078 self.display_tctrl_as_valid(tctrl = self._TCTRL_factor, valid = False)
1079 self._TCTRL_factor.SetFocus()
1080 else:
1081 self.display_tctrl_as_valid(tctrl = self._TCTRL_factor, valid = True)
1082
1083 if self._TCTRL_amount.GetValue().strip() == u'':
1084 validity = False
1085 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
1086 self._TCTRL_amount.SetFocus()
1087 else:
1088 converted, factor = gmTools.input2decimal(self._TCTRL_amount.GetValue())
1089 if not converted:
1090 validity = False
1091 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
1092 self._TCTRL_amount.SetFocus()
1093 else:
1094 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
1095
1096 if self._TCTRL_count.GetValue().strip() == u'':
1097 validity = False
1098 self.display_tctrl_as_valid(tctrl = self._TCTRL_count, valid = False)
1099 self._TCTRL_count.SetFocus()
1100 else:
1101 converted, factor = gmTools.input2decimal(self._TCTRL_count.GetValue())
1102 if not converted:
1103 validity = False
1104 self.display_tctrl_as_valid(tctrl = self._TCTRL_count, valid = False)
1105 self._TCTRL_count.SetFocus()
1106 else:
1107 self.display_tctrl_as_valid(tctrl = self._TCTRL_count, valid = True)
1108
1109 if self._PRW_date.is_valid_timestamp(allow_empty = True):
1110 self._PRW_date.display_as_valid(True)
1111 else:
1112 validity = False
1113 self._PRW_date.display_as_valid(False)
1114 self._PRW_date.SetFocus()
1115
1116 if self._PRW_encounter.GetData() is None:
1117 validity = False
1118 self._PRW_encounter.display_as_valid(False)
1119 self._PRW_encounter.SetFocus()
1120 else:
1121 self._PRW_encounter.display_as_valid(True)
1122
1123 if self._PRW_billable.GetData() is None:
1124 validity = False
1125 self._PRW_billable.display_as_valid(False)
1126 self._PRW_billable.SetFocus()
1127 else:
1128 self._PRW_billable.display_as_valid(True)
1129
1130 return validity
1131
1147
1156
1169
1171 self._PRW_billable.SetText()
1172 self._TCTRL_count.SetValue(u'1')
1173 self._TCTRL_amount.SetValue(u'')
1174 self._TCTRL_comment.SetValue(u'')
1175
1176 self._PRW_billable.Enable()
1177 self._PRW_billable.SetFocus()
1178
1191
1193 if item is None:
1194 return
1195 if self._TCTRL_amount.GetValue().strip() != u'':
1196 return
1197 val = u'%s' % self._PRW_billable.GetData(as_instance = True)['raw_amount']
1198 wx.CallAfter(self._TCTRL_amount.SetValue, val)
1199
1200
1201
1202
1203 from Gnumed.wxGladeWidgets import wxgBillingPluginPnl
1204
1205 -class cBillingPluginPnl(wxgBillingPluginPnl.wxgBillingPluginPnl, gmRegetMixin.cRegetOnPaintMixin):
1211
1213 self._PNL_bill_items.identity = None
1214 self._CHBOX_show_non_invoiced_only.SetValue(1)
1215 self._PRW_billable.SetText(u'', None)
1216 self._TCTRL_factor.SetValue(u'1.0')
1217 self._TCTRL_factor.Disable()
1218 self._TCTRL_details.SetValue(u'')
1219 self._TCTRL_details.Disable()
1220
1221
1222
1230
1232 wx.CallAfter(self.__reset_ui)
1233
1235 wx.CallAfter(self._schedule_data_reget)
1236
1238 wx.CallAfter(self._schedule_data_reget)
1239
1242
1267
1269 if billable is None:
1270 self._TCTRL_factor.Disable()
1271 self._TCTRL_details.Disable()
1272 self._BTN_insert_item.Disable()
1273 else:
1274 self._TCTRL_factor.Enable()
1275 self._TCTRL_details.Enable()
1276 self._BTN_insert_item.Enable()
1277
1278
1279
1283
1284
1285
1286 if __name__ == '__main__':
1287
1288 if len(sys.argv) < 2:
1289 sys.exit()
1290
1291 if sys.argv[1] != 'test':
1292 sys.exit()
1293
1294 from Gnumed.pycommon import gmI18N
1295 gmI18N.activate_locale()
1296 gmI18N.install_domain(domain = 'gnumed')
1297
1298
1299 app = wx.PyWidgetTester(size = (600, 600))
1300
1301
1302 app.MainLoop()
1303