Package Gnumed :: Package wxpython :: Module gmBillingWidgets
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmBillingWidgets

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