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

Source Code for Module Gnumed.wxpython.gmBillingWidgets

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