| 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.DestroyLater()
64 return True
65 dlg.DestroyLater()
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.StatusText = _('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.StatusText = _('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.StatusText = _('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.StatusText = _('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.StatusText = _('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.StatusText = _('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.StatusText = _('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 Exception:
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.DestroyLater()
446 return True
447 dlg.DestroyLater()
448 return False
449
450 #----------------------------------------------------------------
452
453 if len(bill_items) == 0:
454 return None
455
456 item = bill_items[0]
457 currency = item['currency']
458 vat = item['vat_multiplier']
459 pk_pat = item['pk_patient']
460
461 # check item consistency
462 has_errors = False
463 for item in bill_items:
464 if item['pk_bill'] is not None:
465 msg = _(
466 'This item is already invoiced:\n'
467 '\n'
468 '%s\n'
469 '\n'
470 'Cannot put it on a second bill.'
471 ) % item.format()
472 has_errors = True
473 break
474 if (item['currency'] != currency) or (
475 item['vat_multiplier'] != vat) or (
476 item['pk_patient'] != pk_pat
477 ):
478 msg = _(
479 'All items to be included with a bill must\n'
480 'coincide on currency, VAT, and patient.\n'
481 '\n'
482 'This item does not:\n'
483 '\n'
484 '%s\n'
485 ) % item.format()
486 has_errors = True
487 break
488 if has_errors:
489 gmGuiHelpers.gm_show_warning(aTitle = _('Checking invoice items'), aMessage = msg)
490 return None
491
492 # create bill
493 person = gmPerson.cPerson(pk_pat)
494 dbcfg = gmCfg.cCfgSQL()
495 invoice_id_template = dbcfg.get2 (
496 option = u'billing.invoice_id_template',
497 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
498 bias = 'user'
499 )
500 invoice_id = None
501 max_attempts = 3
502 attempt = 0
503 while (invoice_id is None) and (attempt < max_attempts+1):
504 attempt += 1
505 invoice_id = gmBilling.generate_invoice_id(template = invoice_id_template, person = person)
506 if invoice_id is None:
507 continue
508 if gmBilling.lock_invoice_id(invoice_id):
509 break
510 invoice_id = None
511 if invoice_id is None:
512 gmGuiHelpers.gm_show_warning (
513 aTitle = _('Generating bill'),
514 aMessage = _('Could not generate invoice ID.\n\nTry again later.')
515 )
516 return None
517
518 bill = gmBilling.create_bill(invoice_id = invoice_id)
519 gmBilling.unlock_invoice_id(invoice_id)
520 _log.info('created bill [%s]', bill['invoice_id'])
521 bill.add_items(items = bill_items)
522 bill.set_missing_address_from_default()
523
524 return bill
525
526 #----------------------------------------------------------------
528
529 bill_patient_not_active = False
530 # do we have a current patient ?
531 curr_pat = gmPerson.gmCurrentPatient()
532 if curr_pat.connected:
533 # is the bill about the current patient, too ?
534 # (because that's what the new invoice would get
535 # created for and attached to)
536 if curr_pat.ID != bill['pk_patient']:
537 bill_patient_not_active = True
538 else:
539 bill_patient_not_active = True
540
541 # FIXME: could ask whether to set fk_receiver_identity
542 # FIXME: but this would need enabling the bill EA to edit same
543 if bill_patient_not_active:
544 activate_patient = gmGuiHelpers.gm_show_question (
545 title = _('Creating invoice'),
546 question = _(
547 'Cannot find an existing invoice PDF for this bill.\n'
548 '\n'
549 'Active patient: %s\n'
550 'Patient on bill: #%s\n'
551 '\n'
552 'Activate patient on bill so invoice PDF can be created ?'
553 ) % (
554 gmTools.coalesce(curr_pat.ID, '', '#%s'),
555 bill['pk_patient']
556 )
557 )
558 if not activate_patient:
559 return False
560 if not gmPatSearchWidgets.set_active_patient(patient = bill['pk_patient']):
561 gmGuiHelpers.gm_show_error (
562 aTitle = _('Creating invoice'),
563 aMessage = _('Cannot activate patient #%s.') % bill['pk_patient']
564 )
565 return False
566
567 if None in [ bill['close_date'], bill['pk_receiver_address'], bill['apply_vat'] ]:
568 edit_bill(parent = parent, bill = bill, single_entry = True)
569 # cannot invoice open bills
570 if bill['close_date'] is None:
571 _log.error('cannot create invoice from bill, bill not closed')
572 gmGuiHelpers.gm_show_warning (
573 aTitle = _('Creating invoice'),
574 aMessage = _(
575 'Cannot create invoice from bill.\n'
576 '\n'
577 'The bill is not closed.'
578 )
579 )
580 return False
581 # cannot create invoice if no receiver address
582 if bill['pk_receiver_address'] is None:
583 _log.error('cannot create invoice from bill, lacking receiver address')
584 gmGuiHelpers.gm_show_warning (
585 aTitle = _('Creating invoice'),
586 aMessage = _(
587 'Cannot create invoice from bill.\n'
588 '\n'
589 'There is no receiver address.'
590 )
591 )
592 return False
593 # cannot create invoice if applying VAT is undecided
594 if bill['apply_vat'] is None:
595 _log.error('cannot create invoice from bill, apply_vat undecided')
596 gmGuiHelpers.gm_show_warning (
597 aTitle = _('Creating invoice'),
598 aMessage = _(
599 'Cannot create invoice from bill.\n'
600 '\n'
601 'You must decide on whether to apply VAT.'
602 )
603 )
604 return False
605
606 # find template
607 template = get_invoice_template(parent = parent, with_vat = bill['apply_vat'])
608 if template is None:
609 gmGuiHelpers.gm_show_warning (
610 aTitle = _('Creating invoice'),
611 aMessage = _(
612 'Cannot create invoice from bill\n'
613 'without an invoice template.'
614 )
615 )
616 return False
617
618 # process template
619 try:
620 invoice = template.instantiate()
621 except KeyError:
622 _log.exception('cannot instantiate invoice template [%s]', template)
623 gmGuiHelpers.gm_show_error (
624 aMessage = _('Invalid invoice template [%s - %s (%s)]') % (name, ver, template['engine']),
625 aTitle = _('Printing medication list')
626 )
627 return False
628
629 ph = gmMacro.gmPlaceholderHandler()
630 #ph.debug = True
631 ph.set_cache_value('bill', bill)
632 invoice.substitute_placeholders(data_source = ph)
633 ph.unset_cache_value('bill')
634 pdf_name = invoice.generate_output()
635 if pdf_name is None:
636 gmGuiHelpers.gm_show_error (
637 aMessage = _('Error generating invoice PDF.'),
638 aTitle = _('Creating invoice')
639 )
640 return False
641
642 # keep a copy
643 if keep_a_copy:
644 files2import = []
645 files2import.extend(invoice.final_output_filenames)
646 files2import.extend(invoice.re_editable_filenames)
647 doc = gmDocumentWidgets.save_files_as_new_document (
648 parent = parent,
649 filenames = files2import,
650 document_type = template['instance_type'],
651 review_as_normal = True,
652 reference = bill['invoice_id'],
653 pk_org_unit = gmPraxis.gmCurrentPraxisBranch()['pk_org_unit']
654 )
655 bill['pk_doc'] = doc['pk_doc']
656 bill.save()
657
658 if not print_it:
659 return True
660
661 # print template
662 _cfg = gmCfg2.gmCfgData()
663 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'invoice', verbose = _cfg.get(option = 'debug'))
664 if not printed:
665 gmGuiHelpers.gm_show_error (
666 aMessage = _('Error printing the invoice.'),
667 aTitle = _('Printing invoice')
668 )
669 return True
670
671 return True
672
673 #----------------------------------------------------------------
675
676 if parent is None:
677 parent = wx.GetApp().GetTopWindow()
678
679 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
680 parent, -1,
681 caption = _('Deleting bill'),
682 question = _(
683 'When deleting the bill [%s]\n'
684 'do you want to keep its items (effectively \"unbilling\" them)\n'
685 'or do you want to also delete the bill items from the patient ?\n'
686 ) % bill['invoice_id'],
687 button_defs = [
688 {'label': _('Delete + keep'), 'tooltip': _('Delete the bill but keep ("unbill") its items.'), 'default': True},
689 {'label': _('Delete all'), 'tooltip': _('Delete both the bill and its items from the patient.')}
690 ],
691 show_checkbox = True,
692 checkbox_msg = _('Also remove invoice PDF'),
693 checkbox_tooltip = _('Also remove the invoice PDF from the document archive (because it will not correspond to the bill anymore).')
694 )
695 button_pressed = dlg.ShowModal()
696 delete_invoice = dlg.checkbox_is_checked()
697 dlg.DestroyLater()
698
699 if button_pressed == wx.ID_CANCEL:
700 return False
701
702 delete_items = (button_pressed == wx.ID_NO)
703
704 if delete_invoice:
705 if bill['pk_doc'] is not None:
706 gmDocuments.delete_document (
707 document_id = bill['pk_doc'],
708 encounter_id = gmPerson.cPatient(aPK_obj = bill['pk_patient']).emr.active_encounter['pk_encounter']
709 )
710
711 items = bill['pk_bill_items']
712 success = gmBilling.delete_bill(pk_bill = bill['pk_bill'])
713 if delete_items:
714 for item in items:
715 gmBilling.delete_bill_item(pk_bill_item = item)
716
717 return success
718
719 #----------------------------------------------------------------
721
722 if bill is None:
723 return False
724
725 list_data = bill.bill_items
726 if len(list_data) == 0:
727 return False
728
729 if parent is None:
730 parent = wx.GetApp().GetTopWindow()
731
732 list_items = [ [
733 gmDateTime.pydt_strftime(b['date_to_bill'], '%Y %b %d', accuracy = gmDateTime.acc_days),
734 b['unit_count'],
735 '%s: %s%s' % (b['billable_code'], b['billable_description'], gmTools.coalesce(b['item_detail'], '', ' - %s')),
736 '%(curr)s %(total_val)s (%(count)s %(x)s %(unit_val)s%(x)s%(val_multiplier)s)' % {
737 'curr': b['currency'],
738 'total_val': b['total_amount'],
739 'count': b['unit_count'],
740 'x': gmTools.u_multiply,
741 'unit_val': b['net_amount_per_unit'],
742 'val_multiplier': b['amount_multiplier']
743 },
744 '%(curr)s%(vat)s (%(perc_vat)s%%)' % {
745 'vat': b['vat'],
746 'curr': b['currency'],
747 'perc_vat': b['vat_multiplier'] * 100
748 },
749 '%s (%s)' % (b['catalog_short'], b['catalog_version']),
750 b['pk_bill_item']
751 ] for b in list_data ]
752
753 msg = _('Select the items you want to remove from bill [%s]:\n') % bill['invoice_id']
754 items2remove = gmListWidgets.get_choices_from_list (
755 parent = parent,
756 msg = msg,
757 caption = _('Removing items from bill'),
758 columns = [_('Date'), _('Count'), _('Description'), _('Value'), _('VAT'), _('Catalog'), '#'],
759 single_selection = False,
760 choices = list_items,
761 data = list_data
762 )
763
764 if items2remove is None:
765 return False
766
767 if len(items2remove) == len(list_items):
768 gmGuiHelpers.gm_show_info (
769 title = _('Removing items from bill'),
770 info = _(
771 'Cannot remove all items from a bill because\n'
772 'GNUmed does not support empty bills.\n'
773 '\n'
774 'You must delete the bill itself if you want to\n'
775 'remove all items (at which point you can opt to\n'
776 'keep the items and only delete the bill).'
777 )
778 )
779 return False
780
781 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
782 parent, -1,
783 caption = _('Removing items from bill'),
784 question = _(
785 '%s items selected from bill [%s]\n'
786 '\n'
787 'Do you want to only remove the selected items\n'
788 'from the bill ("unbill" them) or do you want\n'
789 'to delete them entirely from the patient ?\n'
790 '\n'
791 'Note that neither action is reversible.'
792 ) % (
793 len(items2remove),
794 bill['invoice_id']
795 ),
796 button_defs = [
797 {'label': _('"Unbill"'), 'tooltip': _('Only "unbill" items (remove from bill but do not delete from patient).'), 'default': True},
798 {'label': _('Delete'), 'tooltip': _('Completely delete items from the patient.')}
799 ],
800 show_checkbox = True,
801 checkbox_msg = _('Also remove invoice PDF'),
802 checkbox_tooltip = _('Also remove the invoice PDF from the document archive (because it will not correspond to the bill anymore).')
803 )
804 button_pressed = dlg.ShowModal()
805 delete_invoice = dlg.checkbox_is_checked()
806 dlg.DestroyLater()
807
808 if button_pressed == wx.ID_CANCEL:
809 return False
810
811 # remember this because unlinking/deleting the items
812 # will remove the patient PK from the bill
813 pk_patient = bill['pk_patient']
814
815 for item in items2remove:
816 item['pk_bill'] = None
817 item.save()
818 if button_pressed == wx.ID_NO:
819 gmBilling.delete_bill_item(pk_bill_item = item['pk_bill_item'])
820
821 if delete_invoice:
822 if bill['pk_doc'] is not None:
823 gmDocuments.delete_document (
824 document_id = bill['pk_doc'],
825 encounter_id = gmPerson.cPatient(aPK_obj = pk_patient).emr.active_encounter['pk_encounter']
826 )
827
828 # delete bill, too, if empty
829 if len(bill.bill_items) == 0:
830 gmBilling.delete_bill(pk_bill = bill['pk_bill'])
831
832 return True
833
834 #----------------------------------------------------------------
836
837 if parent is None:
838 parent = wx.GetApp().GetTopWindow()
839
840 #------------------------------------------------------------
841 def show_pdf(bill):
842 if bill is None:
843 return False
844
845 # find invoice
846 invoice = bill.invoice
847 if invoice is not None:
848 success, msg = invoice.parts[-1].display_via_mime()
849 if not success:
850 gmGuiHelpers.gm_show_error(aMessage = msg, aTitle = _('Displaying invoice'))
851 return False
852
853 # create it ?
854 create_it = gmGuiHelpers.gm_show_question (
855 title = _('Displaying invoice'),
856 question = _(
857 'Cannot find an existing\n'
858 'invoice PDF for this bill.\n'
859 '\n'
860 'Do you want to create one ?'
861 ),
862 )
863 if not create_it:
864 return False
865
866 # prepare invoicing
867 if not bill.set_missing_address_from_default():
868 gmGuiHelpers.gm_show_warning (
869 aTitle = _('Creating invoice'),
870 aMessage = _(
871 'There is no pre-configured billing address.\n'
872 '\n'
873 'Select the address you want to send the bill to.'
874 )
875 )
876 edit_bill(parent = parent, bill = bill, single_entry = True)
877 if bill['pk_receiver_address'] is None:
878 return False
879 if bill['close_date'] is None:
880 bill['close_date'] = gmDateTime.pydt_now_here()
881 bill.save()
882
883 return create_invoice_from_bill(parent = parent, bill = bill, print_it = True, keep_a_copy = True)
884 #------------------------------------------------------------
885 def edit(bill):
886 return edit_bill(parent = parent, bill = bill, single_entry = True)
887 #------------------------------------------------------------
888 def delete(bill):
889 return delete_bill(parent = parent, bill = bill)
890 #------------------------------------------------------------
891 def remove_items(bill):
892 return remove_items_from_bill(parent = parent, bill = bill)
893 #------------------------------------------------------------
894 def get_tooltip(item):
895 if item is None:
896 return None
897 return item.format()
898 #------------------------------------------------------------
899 def refresh(lctrl):
900 if patient is None:
901 bills = gmBilling.get_bills()
902 else:
903 bills = gmBilling.get_bills(pk_patient = patient.ID)
904 items = []
905 for b in bills:
906 if b['close_date'] is None:
907 close_date = _('<open>')
908 else:
909 close_date = gmDateTime.pydt_strftime(b['close_date'], '%Y %b %d')
910 if b['total_amount'] is None:
911 amount = _('no items on bill')
912 else:
913 amount = gmTools.bool2subst (
914 b['apply_vat'],
915 _('%(currency)s%(total_amount_with_vat)s (with %(percent_vat)s%% VAT)') % b,
916 '%(currency)s%(total_amount)s' % b,
917 _('without VAT: %(currency)s%(total_amount)s / with %(percent_vat)s%% VAT: %(currency)s%(total_amount_with_vat)s') % b
918 )
919 items.append ([
920 close_date,
921 b['invoice_id'],
922 amount,
923 gmTools.coalesce(b['comment'], '')
924 ])
925 lctrl.set_string_items(items)
926 lctrl.set_data(bills)
927 #------------------------------------------------------------
928 return gmListWidgets.get_choices_from_list (
929 parent = parent,
930 caption = _('Showing bills.'),
931 columns = [_('Close date'), _('Invoice ID'), _('Value'), _('Comment')],
932 single_selection = True,
933 edit_callback = edit,
934 delete_callback = delete,
935 refresh_callback = refresh,
936 middle_extra_button = (
937 'PDF',
938 _('Create if necessary, and show the corresponding invoice PDF'),
939 show_pdf
940 ),
941 right_extra_button = (
942 _('Unbill'),
943 _('Select and remove items from a bill.'),
944 remove_items
945 ),
946 list_tooltip_callback = get_tooltip
947 )
948
949 #----------------------------------------------------------------
950 from Gnumed.wxGladeWidgets import wxgBillEAPnl
951
953
955
956 try:
957 data = kwargs['bill']
958 del kwargs['bill']
959 except KeyError:
960 data = None
961
962 wxgBillEAPnl.wxgBillEAPnl.__init__(self, *args, **kwargs)
963 gmEditArea.cGenericEditAreaMixin.__init__(self)
964
965 self.mode = 'new'
966 self.data = data
967 if data is not None:
968 self.mode = 'edit'
969
970 self._3state2bool = {
971 wx.CHK_UNCHECKED: False,
972 wx.CHK_CHECKED: True,
973 wx.CHK_UNDETERMINED: None
974 }
975 self.bool_to_3state = {
976 False: wx.CHK_UNCHECKED,
977 True: wx.CHK_CHECKED,
978 None: wx.CHK_UNDETERMINED
979 }
980
981 # self.__init_ui()
982 #----------------------------------------------------------------
983 # def __init_ui(self):
984 #----------------------------------------------------------------
985 # generic Edit Area mixin API
986 #----------------------------------------------------------------
988 validity = True
989
990 # flag but do not count as wrong
991 if not self._PRW_close_date.is_valid_timestamp(empty_is_valid = False):
992 self._PRW_close_date.SetFocus()
993
994 # flag but do not count as wrong
995 if self._CHBOX_vat_applies.ThreeStateValue == wx.CHK_UNDETERMINED:
996 self._CHBOX_vat_applies.SetFocus()
997 self._CHBOX_vat_applies.SetBackgroundColour('yellow')
998
999 return validity
1000 #----------------------------------------------------------------
1004 #----------------------------------------------------------------
1006 self.data['close_date'] = self._PRW_close_date.GetData()
1007 self.data['apply_vat'] = self._3state2bool[self._CHBOX_vat_applies.ThreeStateValue]
1008 self.data['comment'] = self._TCTRL_comment.GetValue()
1009 self.data.save()
1010 return True
1011 #----------------------------------------------------------------
1014 #----------------------------------------------------------------
1017 #----------------------------------------------------------------
1019 self._TCTRL_invoice_id.SetValue(self.data['invoice_id'])
1020 self._PRW_close_date.SetText(data = self.data['close_date'])
1021
1022 self.data.set_missing_address_from_default()
1023 if self.data['pk_receiver_address'] is None:
1024 self._TCTRL_address.SetValue('')
1025 else:
1026 adr = self.data.address
1027 self._TCTRL_address.SetValue(adr.format(single_line = True, show_type = False))
1028
1029 self._TCTRL_value.SetValue('%(currency)s%(total_amount)s' % self.data)
1030 self._CHBOX_vat_applies.ThreeStateValue = self.bool_to_3state[self.data['apply_vat']]
1031 self._CHBOX_vat_applies.SetLabel(_('&VAT applies (%s%%)') % self.data['percent_vat'])
1032 if self.data['apply_vat'] is True:
1033 tmp = '%s %%(currency)s%%(total_vat)s %s %s %%(currency)s%%(total_amount_with_vat)s' % (
1034 gmTools.u_corresponds_to,
1035 gmTools.u_arrow2right,
1036 gmTools.u_sum,
1037 )
1038 self._TCTRL_value_with_vat.SetValue(tmp % self.data)
1039 elif self.data['apply_vat'] is None:
1040 self._TCTRL_value_with_vat.SetValue('?')
1041 else:
1042 self._TCTRL_value_with_vat.SetValue('')
1043
1044 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
1045
1046 self._PRW_close_date.SetFocus()
1047 #----------------------------------------------------------------
1048 # event handling
1049 #----------------------------------------------------------------
1051 if self._CHBOX_vat_applies.ThreeStateValue == wx.CHK_CHECKED:
1052 tmp = '%s %%(currency)s%%(total_vat)s %s %s %%(currency)s%%(total_amount_with_vat)s' % (
1053 gmTools.u_corresponds_to,
1054 gmTools.u_arrow2right,
1055 gmTools.u_sum,
1056 )
1057 self._TCTRL_value_with_vat.SetValue(tmp % self.data)
1058 return
1059 if self._CHBOX_vat_applies.ThreeStateValue == wx.CHK_UNDETERMINED:
1060 self._TCTRL_value_with_vat.SetValue('?')
1061 return
1062 self._TCTRL_value_with_vat.SetValue('')
1063 #----------------------------------------------------------------
1078
1079 #================================================================
1080 # per-patient bill items related widgets
1081 #----------------------------------------------------------------
1083
1084 if bill_item is not None:
1085 if bill_item.is_in_use:
1086 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit already invoiced bill item.'), beep = True)
1087 return False
1088
1089 ea = cBillItemEAPnl(parent, -1)
1090 ea.data = bill_item
1091 ea.mode = gmTools.coalesce(bill_item, 'new', 'edit')
1092 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
1093 dlg.SetTitle(gmTools.coalesce(bill_item, _('Adding new bill item'), _('Editing bill item')))
1094 if dlg.ShowModal() == wx.ID_OK:
1095 dlg.DestroyLater()
1096 return True
1097 dlg.DestroyLater()
1098 return False
1099 #----------------------------------------------------------------
1101
1102 if parent is None:
1103 parent = wx.GetApp().GetTopWindow()
1104 #------------------------------------------------------------
1105 def edit(item=None):
1106 return edit_bill_item(parent = parent, bill_item = item, single_entry = (item is not None))
1107 #------------------------------------------------------------
1108 def delete(item):
1109 if item.is_in_use is not None:
1110 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete already invoiced bill items.'), beep = True)
1111 return False
1112 gmBilling.delete_bill_item(pk_bill_item = item['pk_bill_item'])
1113 return True
1114 #------------------------------------------------------------
1115 def get_tooltip(item):
1116 if item is None:
1117 return None
1118 return item.format()
1119 #------------------------------------------------------------
1120 def refresh(lctrl):
1121 b_items = gmBilling.get_bill_items(pk_patient = pk_patient)
1122 items = [ [
1123 gmDateTime.pydt_strftime(b['date_to_bill'], '%Y %b %d', accuracy = gmDateTime.acc_days),
1124 b['unit_count'],
1125 '%s: %s%s' % (b['billable_code'], b['billable_description'], gmTools.coalesce(b['item_detail'], '', ' - %s')),
1126 b['currency'],
1127 '%s (%s %s %s%s%s)' % (
1128 b['total_amount'],
1129 b['unit_count'],
1130 gmTools.u_multiply,
1131 b['net_amount_per_unit'],
1132 gmTools.u_multiply,
1133 b['amount_multiplier']
1134 ),
1135 '%s (%s%%)' % (
1136 b['vat'],
1137 b['vat_multiplier'] * 100
1138 ),
1139 '%s (%s)' % (b['catalog_short'], b['catalog_version']),
1140 b['pk_bill_item']
1141 ] for b in b_items ]
1142 lctrl.set_string_items(items)
1143 lctrl.set_data(b_items)
1144 #------------------------------------------------------------
1145 gmListWidgets.get_choices_from_list (
1146 parent = parent,
1147 #msg = msg,
1148 caption = _('Showing bill items.'),
1149 columns = [_('Date'), _('Count'), _('Description'), _('$__replace_by_your_currency_symbol')[:-len('__replace_by_your_currency_symbol')], _('Value'), _('VAT'), _('Catalog'), '#'],
1150 single_selection = True,
1151 new_callback = edit,
1152 edit_callback = edit,
1153 delete_callback = delete,
1154 refresh_callback = refresh,
1155 list_tooltip_callback = get_tooltip
1156 )
1157
1158 #------------------------------------------------------------
1160 """A list for managing a patient's bill items.
1161
1162 Does NOT act on/listen to the current patient.
1163 """
1165
1166 try:
1167 self.__identity = kwargs['identity']
1168 del kwargs['identity']
1169 except KeyError:
1170 self.__identity = None
1171
1172 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
1173
1174 self.refresh_callback = self.refresh
1175 self.new_callback = self._add_item
1176 self.edit_callback = self._edit_item
1177 self.delete_callback = self._del_item
1178
1179 self.__show_non_invoiced_only = True
1180
1181 self.__init_ui()
1182 self.refresh()
1183 #--------------------------------------------------------
1184 # external API
1185 #--------------------------------------------------------
1187 if self.__identity is None:
1188 self._LCTRL_items.set_string_items()
1189 return
1190
1191 b_items = gmBilling.get_bill_items(pk_patient = self.__identity.ID, non_invoiced_only = self.__show_non_invoiced_only)
1192 items = [ [
1193 gmDateTime.pydt_strftime(b['date_to_bill'], '%Y %b %d', accuracy = gmDateTime.acc_days),
1194 b['unit_count'],
1195 '%s: %s%s' % (b['billable_code'], b['billable_description'], gmTools.coalesce(b['item_detail'], '', ' - %s')),
1196 b['currency'],
1197 b['total_amount'],
1198 '%s (%s%%)' % (
1199 b['vat'],
1200 b['vat_multiplier'] * 100
1201 ),
1202 '%s (%s)' % (b['catalog_short'], b['catalog_version']),
1203 '%s %s %s %s %s' % (
1204 b['unit_count'],
1205 gmTools.u_multiply,
1206 b['net_amount_per_unit'],
1207 gmTools.u_multiply,
1208 b['amount_multiplier']
1209 ),
1210 gmTools.coalesce(b['pk_bill'], gmTools.u_diameter),
1211 b['pk_encounter_to_bill'],
1212 b['pk_bill_item']
1213 ] for b in b_items ]
1214
1215 self._LCTRL_items.set_string_items(items = items)
1216 self._LCTRL_items.set_column_widths()
1217 self._LCTRL_items.set_data(data = b_items)
1218 #--------------------------------------------------------
1219 # internal helpers
1220 #--------------------------------------------------------
1222 self._LCTRL_items.set_columns(columns = [
1223 _('Charge date'),
1224 _('Count'),
1225 _('Description'),
1226 _('$__replace_by_your_currency_symbol')[:-len('__replace_by_your_currency_symbol')],
1227 _('Value'),
1228 _('VAT'),
1229 _('Catalog'),
1230 _('Count %s Value %s Factor') % (gmTools.u_multiply, gmTools.u_multiply),
1231 _('Invoice'),
1232 _('Encounter'),
1233 '#'
1234 ])
1235 self._LCTRL_items.item_tooltip_callback = self._get_item_tooltip
1236 # self.left_extra_button = (
1237 # _('Select pending'),
1238 # _('Select non-invoiced (pending) items.'),
1239 # self._select_pending_items
1240 # )
1241 self.left_extra_button = (
1242 _('Invoice selected items'),
1243 _('Create invoice from selected items.'),
1244 self._invoice_selected_items
1245 )
1246 self.middle_extra_button = (
1247 _('Bills'),
1248 _('Browse bills of this patient.'),
1249 self._browse_bills
1250 )
1251 self.right_extra_button = (
1252 _('Billables'),
1253 _('Browse list of billables.'),
1254 self._browse_billables
1255 )
1256 #--------------------------------------------------------
1259 #--------------------------------------------------------
1262 #--------------------------------------------------------
1264 if item['pk_bill'] is not None:
1265 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete already invoiced bill items.'), beep = True)
1266 return False
1267 go_ahead = gmGuiHelpers.gm_show_question (
1268 _( 'Do you really want to delete this\n'
1269 'bill item from the patient ?'),
1270 _('Deleting bill item')
1271 )
1272 if not go_ahead:
1273 return False
1274 gmBilling.delete_bill_item(pk_bill_item = item['pk_bill_item'])
1275 return True
1276 #--------------------------------------------------------
1281 #--------------------------------------------------------
1284 #--------------------------------------------------------
1286 bill_items = self._LCTRL_items.get_selected_item_data()
1287 bill = create_bill_from_items(bill_items)
1288 if bill is None:
1289 return
1290 if bill['pk_receiver_address'] is None:
1291 gmGuiHelpers.gm_show_error (
1292 aMessage = _(
1293 'Cannot create invoice.\n'
1294 '\n'
1295 'No receiver address selected.'
1296 ),
1297 aTitle = _('Creating invoice')
1298 )
1299 return
1300 if bill['close_date'] is None:
1301 bill['close_date'] = gmDateTime.pydt_now_here()
1302 bill.save()
1303 create_invoice_from_bill(parent = self, bill = bill, print_it = True, keep_a_copy = True)
1304 #--------------------------------------------------------
1308 #--------------------------------------------------------
1311 #--------------------------------------------------------
1312 # properties
1313 #--------------------------------------------------------
1316
1320
1321 identity = property(_get_identity, _set_identity)
1322 #--------------------------------------------------------
1325
1329
1330 show_non_invoiced_only = property(_get_show_non_invoiced_only, _set_show_non_invoiced_only)
1331
1332 #------------------------------------------------------------
1333 from Gnumed.wxGladeWidgets import wxgBillItemEAPnl
1334
1336
1338
1339 try:
1340 data = kwargs['bill_item']
1341 del kwargs['bill_item']
1342 except KeyError:
1343 data = None
1344
1345 wxgBillItemEAPnl.wxgBillItemEAPnl.__init__(self, *args, **kwargs)
1346 gmEditArea.cGenericEditAreaMixin.__init__(self)
1347
1348 self.mode = 'new'
1349 self.data = data
1350 if data is not None:
1351 self.mode = 'edit'
1352
1353 self.__init_ui()
1354 #----------------------------------------------------------------
1356 self._PRW_encounter.set_context(context = 'patient', val = gmPerson.gmCurrentPatient().ID)
1357 self._PRW_billable.add_callback_on_selection(self._on_billable_selected)
1358 self._PRW_billable.add_callback_on_modified(self._on_billable_modified)
1359 #----------------------------------------------------------------
1360 # generic Edit Area mixin API
1361 #----------------------------------------------------------------
1363
1364 validity = True
1365
1366 if self._TCTRL_factor.GetValue().strip() == '':
1367 validity = False
1368 self.display_tctrl_as_valid(tctrl = self._TCTRL_factor, valid = False)
1369 self._TCTRL_factor.SetFocus()
1370 else:
1371 converted, factor = gmTools.input2decimal(self._TCTRL_factor.GetValue())
1372 if not converted:
1373 validity = False
1374 self.display_tctrl_as_valid(tctrl = self._TCTRL_factor, valid = False)
1375 self._TCTRL_factor.SetFocus()
1376 else:
1377 self.display_tctrl_as_valid(tctrl = self._TCTRL_factor, valid = True)
1378
1379 if self._TCTRL_amount.GetValue().strip() == '':
1380 validity = False
1381 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
1382 self._TCTRL_amount.SetFocus()
1383 else:
1384 converted, factor = gmTools.input2decimal(self._TCTRL_amount.GetValue())
1385 if not converted:
1386 validity = False
1387 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
1388 self._TCTRL_amount.SetFocus()
1389 else:
1390 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
1391
1392 if self._TCTRL_count.GetValue().strip() == '':
1393 validity = False
1394 self.display_tctrl_as_valid(tctrl = self._TCTRL_count, valid = False)
1395 self._TCTRL_count.SetFocus()
1396 else:
1397 converted, factor = gmTools.input2decimal(self._TCTRL_count.GetValue())
1398 if not converted:
1399 validity = False
1400 self.display_tctrl_as_valid(tctrl = self._TCTRL_count, valid = False)
1401 self._TCTRL_count.SetFocus()
1402 else:
1403 self.display_tctrl_as_valid(tctrl = self._TCTRL_count, valid = True)
1404
1405 if self._PRW_date.is_valid_timestamp(empty_is_valid = True):
1406 self._PRW_date.display_as_valid(True)
1407 else:
1408 validity = False
1409 self._PRW_date.display_as_valid(False)
1410 self._PRW_date.SetFocus()
1411
1412 if self._PRW_encounter.GetData() is None:
1413 validity = False
1414 self._PRW_encounter.display_as_valid(False)
1415 self._PRW_encounter.SetFocus()
1416 else:
1417 self._PRW_encounter.display_as_valid(True)
1418
1419 if self._PRW_billable.GetData() is None:
1420 validity = False
1421 self._PRW_billable.display_as_valid(False)
1422 self._PRW_billable.SetFocus()
1423 else:
1424 self._PRW_billable.display_as_valid(True)
1425
1426 return validity
1427 #----------------------------------------------------------------
1429 data = gmBilling.create_bill_item (
1430 pk_encounter = self._PRW_encounter.GetData(),
1431 pk_billable = self._PRW_billable.GetData(),
1432 pk_staff = gmStaff.gmCurrentProvider()['pk_staff'] # should be settable !
1433 )
1434 data['raw_date_to_bill'] = self._PRW_date.GetData()
1435 converted, data['unit_count'] = gmTools.input2decimal(self._TCTRL_count.GetValue())
1436 converted, data['net_amount_per_unit'] = gmTools.input2decimal(self._TCTRL_amount.GetValue())
1437 converted, data['amount_multiplier'] = gmTools.input2decimal(self._TCTRL_factor.GetValue())
1438 data['item_detail'] = self._TCTRL_comment.GetValue().strip()
1439 data.save()
1440
1441 self.data = data
1442 return True
1443 #----------------------------------------------------------------
1445 self.data['pk_encounter_to_bill'] = self._PRW_encounter.GetData()
1446 self.data['raw_date_to_bill'] = self._PRW_date.GetData()
1447 converted, self.data['unit_count'] = gmTools.input2decimal(self._TCTRL_count.GetValue())
1448 converted, self.data['net_amount_per_unit'] = gmTools.input2decimal(self._TCTRL_amount.GetValue())
1449 converted, self.data['amount_multiplier'] = gmTools.input2decimal(self._TCTRL_factor.GetValue())
1450 self.data['item_detail'] = self._TCTRL_comment.GetValue().strip()
1451 return self.data.save()
1452 #----------------------------------------------------------------
1454 self._PRW_billable.SetText()
1455 self._PRW_encounter.set_from_instance(gmPerson.gmCurrentPatient().emr.active_encounter)
1456 self._PRW_date.SetData()
1457 self._TCTRL_count.SetValue('1')
1458 self._TCTRL_amount.SetValue('')
1459 self._LBL_currency.SetLabel(gmTools.u_euro)
1460 self._TCTRL_factor.SetValue('1')
1461 self._TCTRL_comment.SetValue('')
1462
1463 self._PRW_billable.Enable()
1464 self._PRW_billable.SetFocus()
1465 #----------------------------------------------------------------
1467 self._PRW_billable.SetText()
1468 self._TCTRL_count.SetValue('1')
1469 self._TCTRL_amount.SetValue('')
1470 self._TCTRL_comment.SetValue('')
1471
1472 self._PRW_billable.Enable()
1473 self._PRW_billable.SetFocus()
1474 #----------------------------------------------------------------
1476 self._PRW_billable.set_from_pk(self.data['pk_billable'])
1477 self._PRW_encounter.SetData(self.data['pk_encounter_to_bill'])
1478 self._PRW_date.SetData(data = self.data['raw_date_to_bill'])
1479 self._TCTRL_count.SetValue('%s' % self.data['unit_count'])
1480 self._TCTRL_amount.SetValue('%s' % self.data['net_amount_per_unit'])
1481 self._LBL_currency.SetLabel(self.data['currency'])
1482 self._TCTRL_factor.SetValue('%s' % self.data['amount_multiplier'])
1483 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['item_detail'], ''))
1484
1485 self._PRW_billable.Disable()
1486 self._PRW_date.SetFocus()
1487 #----------------------------------------------------------------
1489 if item is None:
1490 return
1491 if self._TCTRL_amount.GetValue().strip() != '':
1492 return
1493 val = '%s' % self._PRW_billable.GetData(as_instance = True)['raw_amount']
1494 wx.CallAfter(self._TCTRL_amount.SetValue, val)
1495 #----------------------------------------------------------------
1499
1500 #============================================================
1501 # a plugin for billing
1502 #------------------------------------------------------------
1503 from Gnumed.wxGladeWidgets import wxgBillingPluginPnl
1504
1505 -class cBillingPluginPnl(wxgBillingPluginPnl.wxgBillingPluginPnl, gmRegetMixin.cRegetOnPaintMixin):
1507
1508 wxgBillingPluginPnl.wxgBillingPluginPnl.__init__(self, *args, **kwargs)
1509 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1510 self.__register_interests()
1511 #-----------------------------------------------------
1513 self._PNL_bill_items.identity = None
1514 self._CHBOX_show_non_invoiced_only.SetValue(1)
1515 self._PRW_billable.SetText('', None)
1516 self._TCTRL_factor.SetValue('1.0')
1517 self._TCTRL_factor.Disable()
1518 self._TCTRL_details.SetValue('')
1519 self._TCTRL_details.Disable()
1520 #-----------------------------------------------------
1521 # event handling
1522 #-----------------------------------------------------
1524 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
1525 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
1526
1527 gmDispatcher.connect(signal = 'bill.bill_item_mod_db', receiver = self._on_bill_item_modified)
1528
1529 self._PRW_billable.add_callback_on_selection(self._on_billable_selected_in_prw)
1530 #-----------------------------------------------------
1533 #-----------------------------------------------------
1536 #-----------------------------------------------------
1539 #-----------------------------------------------------
1542 #--------------------------------------------------------
1573 #--------------------------------------------------------
1575 if billable is None:
1576 self._TCTRL_factor.Disable()
1577 self._TCTRL_details.Disable()
1578 self._BTN_insert_item.Disable()
1579 else:
1580 self._TCTRL_factor.Enable()
1581 self._TCTRL_details.Enable()
1582 self._BTN_insert_item.Enable()
1583 #-----------------------------------------------------
1584 # reget-on-paint mixin API
1585 #-----------------------------------------------------
1589
1590 #============================================================
1591 # main
1592 #------------------------------------------------------------
1593 if __name__ == '__main__':
1594
1595 if len(sys.argv) < 2:
1596 sys.exit()
1597
1598 if sys.argv[1] != 'test':
1599 sys.exit()
1600
1601 from Gnumed.pycommon import gmI18N
1602 gmI18N.activate_locale()
1603 gmI18N.install_domain(domain = 'gnumed')
1604
1605 #----------------------------------------
1606 app = wx.PyWidgetTester(size = (600, 600))
1607 #app.SetWidget(cXxxPhraseWheel, -1)
1608 app.MainLoop()
1609
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sat Feb 29 02:55:27 2020 | http://epydoc.sourceforge.net |