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