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

# Source Code for Module Gnumed.wxpython.gmBillingWidgets

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