Gnumed.business.gmBilling

Package Gnumed :: Package business :: Module gmBilling |
[frames] | no frames]

# Source Code for Module Gnumed.business.gmBilling

      1  # -*- coding: utf-8 -*-
      2  """Billing code.
      3
      4  Copyright: authors
      5  """
      6  #============================================================
      7  __author__ = "Nico Latzer <nl@mnet-online.de>, Karsten Hilbert <Karsten.Hilbert@gmx.net>"
      8  __license__ = 'GPL v2 or later (details at http://www.gnu.org)'
      9
     10  import sys
     11  import logging
     12
     13
     14  if __name__ == '__main__':
     15          sys.path.insert(0, '../../')
     16  from Gnumed.pycommon import gmPG2
     17  from Gnumed.pycommon import gmBusinessDBObject
     18  from Gnumed.pycommon import gmTools
     19  from Gnumed.pycommon import gmDateTime
     20  from Gnumed.business import gmDemographicRecord
     21  from Gnumed.business import gmDocuments
     22
     23  _log = logging.getLogger('gm.bill')
     24
     25  INVOICE_DOCUMENT_TYPE = 'invoice'
     26  #============================================================
     27  # billables
     28  #------------------------------------------------------------
     29  _SQL_get_billable_fields = "SELECT * FROM ref.v_billables WHERE %s"
     30
     31 -class cBillable(gmBusinessDBObject.cBusinessDBObject):
     32          """Items which can be billed to patients."""
     33
     34          _cmd_fetch_payload = _SQL_get_billable_fields % "pk_billable = %s"
     35          _cmds_store_payload = [
     36                  """UPDATE ref.billable SET
     37                                  fk_data_source = %(pk_data_source)s,
     38                                  code = %(billable_code)s,
     39                                  term = %(billable_description)s,
     40                                  comment = gm.nullify_empty_string(%(comment)s),
     41                                  amount = %(raw_amount)s,
     42                                  currency = %(currency)s,
     43                                  vat_multiplier = %(vat_multiplier)s,
     44                                  active = %(active)s
     45                                  --, discountable = %(discountable)s
     46                          WHERE
     47                                  pk = %(pk_billable)s
     48                                          AND
     49                                  xmin = %(xmin_billable)s
     50                          RETURNING
     51                                  xmin AS xmin_billable
     52                  """]
     53
     54          _updatable_fields = [
     55                  'billable_code',
     56                  'billable_description',
     57                  'raw_amount',
     58                  'vat_multiplier',
     59                  'comment',
     60                  'currency',
     61                  'active',
     62                  'pk_data_source'
     63          ]
     64          #--------------------------------------------------------
     65 -        def format(self):
     66                  txt = '%s                                    [#%s]\n\n' % (
     67                          gmTools.bool2subst (
     68                                  self._payload[self._idx['active']],
     69                                  _('Active billable item'),
     70                                  _('Inactive billable item')
     71                          ),
     72                          self._payload[self._idx['pk_billable']]
     73                  )
     74                  txt += ' %s: %s\n' % (
     75                          self._payload[self._idx['billable_code']],
     76                          self._payload[self._idx['billable_description']]
     77                  )
     78                  txt += _(' %(curr)s%(raw_val)s + %(perc_vat)s%% VAT = %(curr)s%(val_w_vat)s\n') % {
     79                          'curr': self._payload[self._idx['currency']],
     80                          'raw_val': self._payload[self._idx['raw_amount']],
     81                          'perc_vat': self._payload[self._idx['vat_multiplier']] * 100,
     82                          'val_w_vat': self._payload[self._idx['amount_with_vat']]
     83                  }
     84                  txt += ' %s %s%s (%s)' % (
     85                          self._payload[self._idx['catalog_short']],
     86                          self._payload[self._idx['catalog_version']],
     87                          gmTools.coalesce(self._payload[self._idx['catalog_language']], '', ' - %s'),
     88                          self._payload[self._idx['catalog_long']]
     89                  )
     90                  txt += gmTools.coalesce(self._payload[self._idx['comment']], '', '\n %s')
     91
     92                  return txt
     93          #--------------------------------------------------------
     94 -        def _get_is_in_use(self):
     95                  cmd = 'SELECT EXISTS(SELECT 1 FROM bill.bill_item WHERE fk_billable = %(pk)s LIMIT 1)'
     96                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self._payload[self._idx['pk_billable']]}}])
     97                  return rows[0][0]
     98
     99          is_in_use = property(_get_is_in_use, lambda x:x)
    100
    101  #------------------------------------------------------------
    102 -def get_billables(active_only=True, order_by=None):
    103
    104          if order_by is None:
    105                  order_by = ' ORDER BY catalog_long, catalog_version, billable_code'
    106          else:
    107                  order_by = ' ORDER BY %s' % order_by
    108
    109          if active_only:
    110                  where = 'active IS true'
    111          else:
    112                  where = 'true'
    113
    114          cmd = (_SQL_get_billable_fields % where) + order_by
    115          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
    116          return [ cBillable(row = {'data': r, 'idx': idx, 'pk_field': 'pk_billable'}) for r in rows ]
    117
    118  #------------------------------------------------------------
    119 -def create_billable(code=None, term=None, data_source=None, return_existing=False):
    120          args = {
    121                  'code': code.strip(),
    122                  'term': term.strip(),
    123                  'data_src': data_source
    124          }
    125          cmd = """
    126                  INSERT INTO ref.billable (code, term, fk_data_source)
    127                  SELECT
    128                          %(code)s,
    129                          %(term)s,
    130                          %(data_src)s
    131                  WHERE NOT EXISTS (
    132                          SELECT 1 FROM ref.billable
    133                          WHERE
    134                                  code = %(code)s
    135                                          AND
    136                                  term = %(term)s
    137                                          AND
    138                                  fk_data_source = %(data_src)s
    139                  )
    140                  RETURNING pk"""
    141          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False, return_data = True)
    142          if len(rows) > 0:
    143                  return cBillable(aPK_obj = rows[0]['pk'])
    144
    145          if not return_existing:
    146                  return None
    147
    148          cmd = """
    149                  SELECT * FROM ref.v_billables
    150                  WHERE
    151                          code = %(code)s
    152                                  AND
    153                          term = %(term)s
    154                                  AND
    155                          pk_data_source = %(data_src)s
    156          """
    157          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
    158          return cBillable(row = {'data': rows[0], 'idx': idx, 'pk_field': 'pk_billable'})
    159
    160  #------------------------------------------------------------
    161 -def delete_billable(pk_billable=None):
    162          cmd = """
    163                  DELETE FROM ref.billable
    164                  WHERE
    165                          pk = %(pk)s
    166                                  AND
    167                          NOT EXISTS (
    168                                  SELECT 1 FROM bill.bill_item WHERE fk_billable = %(pk)s
    169                          )
    170          """
    171          args = {'pk': pk_billable}
    172          gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
    173
    174  #============================================================
    175  # bill items
    176  #------------------------------------------------------------
    177  _SQL_get_bill_item_fields = u"SELECT * FROM bill.v_bill_items WHERE %s"
    178
    179 -class cBillItem(gmBusinessDBObject.cBusinessDBObject):
    180
    181          _cmd_fetch_payload = _SQL_get_bill_item_fields % u"pk_bill_item = %s"
    182          _cmds_store_payload = [
    183                  """UPDATE bill.bill_item SET
    184                                  fk_provider = %(pk_provider)s,
    185                                  fk_encounter = %(pk_encounter_to_bill)s,
    186                                  date_to_bill = %(raw_date_to_bill)s,
    187                                  description = gm.nullify_empty_string(%(item_detail)s),
    188                                  net_amount_per_unit = %(net_amount_per_unit)s,
    189                                  currency = gm.nullify_empty_string(%(currency)s),
    190                                  fk_bill = %(pk_bill)s,
    191                                  unit_count = %(unit_count)s,
    192                                  amount_multiplier = %(amount_multiplier)s
    193                          WHERE
    194                                  pk = %(pk_bill_item)s
    195                                          AND
    196                                  xmin = %(xmin_bill_item)s
    197                          RETURNING
    198                                  xmin AS xmin_bill_item
    199                  """]
    200
    201          _updatable_fields = [
    202                  'pk_provider',
    203                  'pk_encounter_to_bill',
    204                  'raw_date_to_bill',
    205                  'item_detail',
    206                  'net_amount_per_unit',
    207                  'currency',
    208                  'pk_bill',
    209                  'unit_count',
    210                  'amount_multiplier'
    211          ]
    212          #--------------------------------------------------------
    213 -        def format(self):
    214                  txt = '%s (%s %s%s)         [#%s]\n' % (
    215                          gmTools.bool2subst(
    216                                  self._payload[self._idx['pk_bill']] is None,
    217                                  _('Open item'),
    218                                  _('Billed item'),
    219                          ),
    220                          self._payload[self._idx['catalog_short']],
    221                          self._payload[self._idx['catalog_version']],
    222                          gmTools.coalesce(self._payload[self._idx['catalog_language']], '', ' - %s'),
    223                          self._payload[self._idx['pk_bill_item']]
    224                  )
    225                  txt += ' %s: %s\n' % (
    226                          self._payload[self._idx['billable_code']],
    227                          self._payload[self._idx['billable_description']]
    228                  )
    229                  txt += gmTools.coalesce (
    230                          self._payload[self._idx['billable_comment']],
    231                          '',
    232                          '  (%s)\n',
    233                  )
    234                  txt += gmTools.coalesce (
    235                          self._payload[self._idx['item_detail']],
    236                          '',
    237                          _(' Details: %s\n'),
    238                  )
    239
    240                  txt += '\n'
    241                  txt += _(' %s of units: %s\n') % (
    242                          gmTools.u_numero,
    243                          self._payload[self._idx['unit_count']]
    244                  )
    245                  txt += _(' Amount per unit: %(curr)s%(val_p_unit)s (%(cat_curr)s%(cat_val)s per catalog)\n') % {
    246                          'curr': self._payload[self._idx['currency']],
    247                          'val_p_unit': self._payload[self._idx['net_amount_per_unit']],
    248                          'cat_curr': self._payload[self._idx['billable_currency']],
    249                          'cat_val': self._payload[self._idx['billable_amount']]
    250                  }
    251                  txt += _(' Amount multiplier: %s\n') % self._payload[self._idx['amount_multiplier']]
    252                  txt += _(' VAT would be: %(perc_vat)s%% %(equals)s %(curr)s%(vat)s\n') % {
    253                          'perc_vat': self._payload[self._idx['vat_multiplier']] * 100,
    254                          'equals': gmTools.u_corresponds_to,
    255                          'curr': self._payload[self._idx['currency']],
    256                          'vat': self._payload[self._idx['vat']]
    257                  }
    258
    259                  txt += '\n'
    260                  txt += _(' Charge date: %s') % gmDateTime.pydt_strftime (
    261                          self._payload[self._idx['date_to_bill']],
    262                          '%Y %b %d',
    263                          accuracy = gmDateTime.acc_days
    264                  )
    265                  bill = self.bill
    266                  if bill is not None:
    267                          txt += _('\n On bill: %s') % bill['invoice_id']
    268
    269                  return txt
    270          #--------------------------------------------------------
    271 -        def _get_billable(self):
    272                  return cBillable(aPK_obj = self._payload[self._idx['pk_billable']])
    273
    274          billable = property(_get_billable, lambda x:x)
    275          #--------------------------------------------------------
    276 -        def _get_bill(self):
    277                  if self._payload[self._idx['pk_bill']] is None:
    278                          return None
    279                  return cBill(aPK_obj = self._payload[self._idx['pk_bill']])
    280
    281          bill = property(_get_bill, lambda x:x)
    282          #--------------------------------------------------------
    283 -        def _get_is_in_use(self):
    284                  return self._payload[self._idx['pk_bill']] is not None
    285
    286          is_in_use = property(_get_is_in_use, lambda x:x)
    287  #------------------------------------------------------------
    288 -def get_bill_items(pk_patient=None, non_invoiced_only=False):
    289          if non_invoiced_only:
    290                  cmd = _SQL_get_bill_item_fields % u"pk_patient = %(pat)s AND pk_bill IS NULL"
    291          else:
    292                  cmd = _SQL_get_bill_item_fields % u"pk_patient = %(pat)s"
    293          args = {'pat': pk_patient}
    294          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
    295          return [ cBillItem(row = {'data': r, 'idx': idx, 'pk_field': 'pk_bill_item'}) for r in rows ]
    296
    297  #------------------------------------------------------------
    298 -def create_bill_item(pk_encounter=None, pk_billable=None, pk_staff=None):
    299
    300          billable = cBillable(aPK_obj = pk_billable)
    301          cmd = """
    302                  INSERT INTO bill.bill_item (
    303                          fk_provider,
    304                          fk_encounter,
    305                          net_amount_per_unit,
    306                          currency,
    307                          fk_billable
    308                  ) VALUES (
    309                          %(staff)s,
    310                          %(enc)s,
    311                          %(val)s,
    312                          %(curr)s,
    313                          %(billable)s
    314                  )
    315                  RETURNING pk"""
    316          args = {
    317                  'staff': pk_staff,
    318                  'enc': pk_encounter,
    319                  'val': billable['raw_amount'],
    320                  'curr': billable['currency'],
    321                  'billable': pk_billable
    322          }
    323          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
    324          return cBillItem(aPK_obj = rows[0][0])
    325
    326  #------------------------------------------------------------
    327 -def delete_bill_item(link_obj=None, pk_bill_item=None):
    328          cmd = 'DELETE FROM bill.bill_item WHERE pk = %(pk)s AND fk_bill IS NULL'
    329          args = {'pk': pk_bill_item}
    330          gmPG2.run_rw_queries(link_obj = link_obj, queries = [{'cmd': cmd, 'args': args}])
    331
    332  #============================================================
    333  # bills
    334  #------------------------------------------------------------
    335  _SQL_get_bill_fields = """SELECT * FROM bill.v_bills WHERE %s"""
    336
    337 -class cBill(gmBusinessDBObject.cBusinessDBObject):
    338          """Represents a bill"""
    339
    340          _cmd_fetch_payload = _SQL_get_bill_fields % "pk_bill = %s"
    341          _cmds_store_payload = [
    342                  """UPDATE bill.bill SET
    343                                  invoice_id = gm.nullify_empty_string(%(invoice_id)s),
    344                                  close_date = %(close_date)s,
    345                                  apply_vat = %(apply_vat)s,
    346                                  comment = gm.nullify_empty_string(%(comment)s),
    347                                  fk_receiver_identity = %(pk_receiver_identity)s,
    348                                  fk_receiver_address = %(pk_receiver_address)s,
    349                                  fk_doc = %(pk_doc)s
    350                          WHERE
    351                                  pk = %(pk_bill)s
    352                                          AND
    353                                  xmin = %(xmin_bill)s
    354                          RETURNING
    355                                  pk as pk_bill,
    356                                  xmin as xmin_bill
    357                  """
    358          ]
    359          _updatable_fields = [
    360                  'invoice_id',
    361                  'pk_receiver_identity',
    362                  'close_date',
    363                  'apply_vat',
    364                  'comment',
    365                  'pk_receiver_address',
    366                  'pk_doc'
    367          ]
    368          #--------------------------------------------------------
    369 -        def format(self, include_receiver=True, include_doc=True):
    370                  txt = '%s                       [#%s]\n' % (
    371                          gmTools.bool2subst (
    372                                  (self._payload[self._idx['close_date']] is None),
    373                                  _('Open bill'),
    374                                  _('Closed bill')
    375                          ),
    376                          self._payload[self._idx['pk_bill']]
    377                  )
    378                  txt += _(' Invoice ID: %s\n') % self._payload[self._idx['invoice_id']]
    379
    380                  if self._payload[self._idx['close_date']] is not None:
    381                          txt += _(' Closed: %s\n') % gmDateTime.pydt_strftime (
    382                                  self._payload[self._idx['close_date']],
    383                                  '%Y %b %d',
    384                                  accuracy = gmDateTime.acc_days
    385                          )
    386
    387                  if self._payload[self._idx['comment']] is not None:
    388                          txt += _(' Comment: %s\n') % self._payload[self._idx['comment']]
    389
    390                  txt += _(' Bill value: %(curr)s%(val)s\n') % {
    391                          'curr': self._payload[self._idx['currency']],
    392                          'val': self._payload[self._idx['total_amount']]
    393                  }
    394
    395                  if self._payload[self._idx['apply_vat']] is None:
    396                          txt += _(' VAT: undecided\n')
    397                  elif self._payload[self._idx['apply_vat']] is True:
    398                          txt += _(' VAT: %(perc_vat)s%% %(equals)s %(curr)s%(vat)s\n') % {
    399                                  'perc_vat': self._payload[self._idx['percent_vat']],
    400                                  'equals': gmTools.u_corresponds_to,
    401                                  'curr': self._payload[self._idx['currency']],
    402                                  'vat': self._payload[self._idx['total_vat']]
    403                          }
    404                          txt += _(' Value + VAT: %(curr)s%(val)s\n') % {
    405                                  'curr': self._payload[self._idx['currency']],
    406                                  'val': self._payload[self._idx['total_amount_with_vat']]
    407                          }
    408                  else:
    409                          txt += _(' VAT: does not apply\n')
    410
    411                  if self._payload[self._idx['pk_bill_items']] is None:
    412                          txt += _(' Items billed: 0\n')
    413                  else:
    414                          txt += _(' Items billed: %s\n') % len(self._payload[self._idx['pk_bill_items']])
    415                  if include_doc:
    416                          txt += _(' Invoice: %s\n') % (
    417                                  gmTools.bool2subst (
    418                                          self._payload[self._idx['pk_doc']] is None,
    419                                          _('not available'),
    420                                          '#%s' % self._payload[self._idx['pk_doc']]
    421                                  )
    422                          )
    423                  txt += _(' Patient: #%s\n') % self._payload[self._idx['pk_patient']]
    424                  if include_receiver:
    425                          txt += gmTools.coalesce (
    426                                  self._payload[self._idx['pk_receiver_identity']],
    427                                  '',
    428                                  _(' Receiver: #%s\n')
    429                          )
    430                          if self._payload[self._idx['pk_receiver_address']] is not None:
    431                                  txt += '\n '.join(gmDemographicRecord.get_patient_address(pk_patient_address = self._payload[self._idx['pk_receiver_address']]).format())
    432
    433                  return txt
    434          #--------------------------------------------------------
    435 -        def add_items(self, items=None):
    436                  """Requires no pending changes within the bill itself."""
    437                  # should check for item consistency first
    438                  conn = gmPG2.get_connection(readonly = False)
    439                  for item in items:
    440                          item['pk_bill'] = self._payload[self._idx['pk_bill']]
    441                          item.save(conn = conn)
    442                  conn.commit()
    443                  self.refetch_payload()          # make sure aggregates are re-filled from view
    444          #--------------------------------------------------------
    445 -        def _get_bill_items(self):
    446                  return [ cBillItem(aPK_obj = pk) for pk in self._payload[self._idx['pk_bill_items']] ]
    447
    448          bill_items = property(_get_bill_items, lambda x:x)
    449          #--------------------------------------------------------
    450 -        def _get_invoice(self):
    451                  if self._payload[self._idx['pk_doc']] is None:
    452                          return None
    453                  return gmDocuments.cDocument(aPK_obj = self._payload[self._idx['pk_doc']])
    454
    455          invoice = property(_get_invoice, lambda x:x)
    456          #--------------------------------------------------------
    457 -        def _get_address(self):
    458                  if self._payload[self._idx['pk_receiver_address']] is None:
    459                          return None
    460                  return gmDemographicRecord.get_address_from_patient_address_pk (
    461                          pk_patient_address = self._payload[self._idx['pk_receiver_address']]
    462                  )
    463
    464          address = property(_get_address, lambda x:x)
    465          #--------------------------------------------------------
    466 -        def _get_default_address(self):
    467                  return gmDemographicRecord.get_patient_address_by_type (
    468                          pk_patient = self._payload[self._idx['pk_patient']],
    469                          adr_type = 'billing'
    470                  )
    471
    472          default_address = property(_get_default_address, lambda x:x)
    473          #--------------------------------------------------------
    474 -        def _get_home_address(self):
    475                  return gmDemographicRecord.get_patient_address_by_type (
    476                          pk_patient = self._payload[self._idx['pk_patient']],
    477                          adr_type = 'home'
    478                  )
    479
    480          home_address = property(_get_home_address, lambda x:x)
    481          #--------------------------------------------------------
    482 -        def set_missing_address_from_default(self):
    483                  if self._payload[self._idx['pk_receiver_address']] is not None:
    484                          return True
    485                  adr = self.default_address
    486                  if adr is None:
    487                          adr = self.home_address
    488                          if adr is None:
    489                                  return False
    490                  self['pk_receiver_address'] = adr['pk_lnk_person_org_address']
    491                  return self.save_payload()
    492
    493  #------------------------------------------------------------
    494 -def get_bills(order_by=None, pk_patient=None):
    495
    496          args = {'pat': pk_patient}
    497          where_parts = ['true']
    498
    499          if pk_patient is not None:
    500                  where_parts.append('pk_patient = %(pat)s')
    501
    502          if order_by is None:
    503                  order_by = ''
    504          else:
    505                  order_by = ' ORDER BY %s' % order_by
    506
    507          cmd = (_SQL_get_bill_fields % ' AND '.join(where_parts)) + order_by
    508          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
    509          return [ cBill(row = {'data': r, 'idx': idx, 'pk_field': 'pk_bill'}) for r in rows ]
    510
    511  #------------------------------------------------------------
    512 -def get_bills4document(pk_document=None):
    513          args = {'pk_doc': pk_document}
    514          cmd = _SQL_get_bill_fields % 'pk_doc = %(pk_doc)s'
    515          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
    516          return [ cBill(row = {'data': r, 'idx': idx, 'pk_field': 'pk_bill'}) for r in rows ]
    517
    518  #------------------------------------------------------------
    519 -def create_bill(conn=None, invoice_id=None):
    520
    521          args = {'inv_id': invoice_id}
    522          cmd = """
    523                  INSERT INTO bill.bill (invoice_id)
    524                  VALUES (gm.nullify_empty_string(%(inv_id)s))
    525                  RETURNING pk
    526          """
    527          rows, idx = gmPG2.run_rw_queries(link_obj = conn, queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
    528
    529          return cBill(aPK_obj = rows[0]['pk'])
    530
    531  #------------------------------------------------------------
    532 -def delete_bill(link_obj=None, pk_bill=None):
    533          args = {'pk': pk_bill}
    534          cmd = "DELETE FROM bill.bill WHERE pk = %(pk)s"
    535          gmPG2.run_rw_queries(link_obj = link_obj, queries = [{'cmd': cmd, 'args': args}])
    536          return True
    537
    538  #------------------------------------------------------------
    539 -def get_bill_receiver(pk_patient=None):
    540          pass
    541
    542  #------------------------------------------------------------
    543 -def get_invoice_id(pk_patient=None):
    544          return 'GM%s / %s' % (
    545                  pk_patient,
    546                  gmDateTime.pydt_strftime (
    547                          gmDateTime.pydt_now_here(),
    548                          '%Y-%m-%d / %H%M%S'
    549                  )
    550          )
    551
    552  #============================================================
    553  # main
    554  #------------------------------------------------------------
    555  if __name__ == "__main__":
    556
    557          if len(sys.argv) < 2:
    558                  sys.exit()
    559
    560          if sys.argv[1] != 'test':
    561                  sys.exit()
    562
    563  #       from Gnumed.pycommon import gmLog2
    564  #       from Gnumed.pycommon import gmI18N
    565  #       from Gnumed.business import gmPerson
    566
    567  #       gmI18N.activate_locale()
    568  ##      gmDateTime.init()
    569
    570 -        def test_default_address():
    571                  bills = get_bills(pk_patient = 12)
    572                  first_bill = bills[0]
    573                  print(first_bill.default_address)
    574
    575 -        def test_me():
    576                  print("--------------")
    577                  me = cBillable(aPK_obj=1)
    578                  fields = me.get_fields()
    579                  for field in fields:
    580                          print(field, ':', me[field])
    581                  print("updatable:", me.get_updatable_fields())
    582                  #me['vat']=4; me.store_payload()
    583          #--------------------------------------------------
    584          #test_me()
    585          test_default_address()
    586