Ejemplo n.º 1
0
class Work(DeactivableMixin, ModelSQL, ModelView):
    'Work'
    __name__ = 'timesheet.work'
    name = fields.Char('Name',
                       states={
                           'invisible': Bool(Eval('origin')),
                           'required': ~Eval('origin'),
                       },
                       depends=['origin'],
                       help="The main identifier of the work.")
    origin = fields.Reference(
        'Origin',
        selection='get_origin',
        states={
            'invisible': Bool(Eval('name')),
            'required': ~Eval('name'),
        },
        depends=['name'],
        help="Use to relate the time spent to other records.")
    duration = fields.Function(
        fields.TimeDelta('Timesheet Duration',
                         'company_work_time',
                         help="Total time spent on this work."),
        'get_duration')
    timesheet_start_date = fields.Date(
        'Timesheet Start',
        domain=[
            If(
                Eval('timesheet_start_date') & Eval('timesheet_end_date'),
                ('timesheet_start_date', '<=', Eval('timesheet_end_date')),
                ()),
        ],
        depends=['timesheet_end_date'],
        help="Restrict adding lines before the date.")
    timesheet_end_date = fields.Date(
        'Timesheet End',
        domain=[
            If(
                Eval('timesheet_start_date') & Eval('timesheet_end_date'),
                ('timesheet_end_date', '>=', Eval('timesheet_start_date')),
                ()),
        ],
        depends=['timesheet_start_date'],
        help="Restrict adding lines after the date.")
    company = fields.Many2One('company.company',
                              'Company',
                              required=True,
                              select=True,
                              help="Make the work belong to the company.")
    timesheet_lines = fields.One2Many('timesheet.line',
                                      'work',
                                      'Timesheet Lines',
                                      depends=['active'],
                                      states={
                                          'readonly':
                                          Not(Bool(Eval('active'))),
                                      },
                                      help="Spend time on this work.")
    # Self referring field to use for aggregation in graph view
    work = fields.Function(fields.Many2One('timesheet.work', 'Work'),
                           'get_work')

    @classmethod
    def __setup__(cls):
        super(Work, cls).__setup__()
        t = cls.__table__()
        cls._sql_constraints += [
            ('origin_unique', Unique(t, t.origin, t.company),
             'The origin must be unique per company.'),
        ]
        cls._error_messages.update({
            'mismatch_company': ('The company of the work "%(work)s" '
                                 'is different than the origin\'s company'),
        })

    @classmethod
    def __register__(cls, module_name):
        table_h = cls.__table_handler__(module_name)
        table = cls.__table__()
        cursor = Transaction().connection.cursor()

        super(Work, cls).__register__(module_name)

        # Migration from 4.0: remove required on name
        table_h.not_null_action('name', 'remove')

        # Migration from 4.0: remove parent, left and right
        if table_h.column_exist('parent'):
            id2name = {}
            id2parent = {}
            cursor.execute(*table.select(table.id, table.parent, table.name))
            for id_, parent, name in cursor:
                id2name[id_] = name
                id2parent[id_] = parent

            for id_, name in id2name.items():
                parent = id2parent[id_]
                while parent:
                    name = '%s\\%s' % (id2name[parent], name)
                    parent = id2parent[parent]
                cursor.execute(
                    *table.update([table.name], [name], where=table.id == id_))
            table_h.drop_column('parent')
        table_h.drop_column('left')
        table_h.drop_column('right')

        # Migration from 4.0: remove timesheet_available
        if table_h.column_exist('timesheet_available'):
            cursor.execute(*table.delete(
                where=table.timesheet_available == False))
            table_h.drop_column('timesheet_available')

    @staticmethod
    def default_company():
        return Transaction().context.get('company')

    @classmethod
    def _get_origin(cls):
        'Return list of Model names for origin Reference'
        return []

    @classmethod
    def get_origin(cls):
        Model = Pool().get('ir.model')
        models = cls._get_origin()
        models = Model.search([
            ('model', 'in', models),
        ])
        return [('', '')] + [(m.model, m.name) for m in models]

    @classmethod
    def get_duration(cls, works, name):
        pool = Pool()
        Line = pool.get('timesheet.line')
        transaction = Transaction()
        cursor = transaction.connection.cursor()
        context = transaction.context

        table_w = cls.__table__()
        line = Line.__table__()
        ids = [w.id for w in works]
        durations = dict.fromkeys(ids, None)
        where = Literal(True)
        if context.get('from_date'):
            where &= line.date >= context['from_date']
        if context.get('to_date'):
            where &= line.date <= context['to_date']
        if context.get('employees'):
            where &= line.employee.in_(context['employees'])

        query_table = table_w.join(line,
                                   'LEFT',
                                   condition=line.work == table_w.id)

        for sub_ids in grouped_slice(ids):
            red_sql = reduce_ids(table_w.id, sub_ids)
            cursor.execute(*query_table.select(table_w.id,
                                               Sum(line.duration),
                                               where=red_sql & where,
                                               group_by=table_w.id))
            for work_id, duration in cursor.fetchall():
                # SQLite uses float for SUM
                if duration and not isinstance(duration, datetime.timedelta):
                    duration = datetime.timedelta(seconds=duration)
                durations[work_id] = duration
        return durations

    def get_work(self, name):
        return self.id

    def get_rec_name(self, name):
        if self.origin:
            return self.origin.rec_name
        else:
            return self.name

    @classmethod
    def search_rec_name(cls, name, clause):
        if clause[1].startswith('!') or clause[1].startswith('not '):
            bool_op = 'AND'
        else:
            bool_op = 'OR'
        return [
            bool_op,
            ('name', ) + tuple(clause[1:]),
        ] + [('origin.rec_name', ) + tuple(clause[1:]) + (origin, )
             for origin in cls._get_origin()]

    @classmethod
    def copy(cls, works, default=None):
        if default is None:
            default = {}
        else:
            default = default.copy()
        default.setdefault('timesheet_lines', None)
        return super(Work, cls).copy(works, default=default)

    @classmethod
    def validate(cls, works):
        super(Work, cls).validate(works)
        for work in works:
            if work.origin and not work._validate_company():
                cls.raise_user_error('mismatch_company', {
                    'work': work.rec_name,
                })

    def _validate_company(self):
        return True

    @classmethod
    def search_global(cls, text):
        for record, rec_name, icon in super(Work, cls).search_global(text):
            icon = icon or 'tryton-clock'
            yield record, rec_name, icon

    @property
    def hours(self):
        if not self.duration:
            return 0
        return self.duration.total_seconds() / 60 / 60
Ejemplo n.º 2
0
class PartyPatient(ModelSQL, ModelView):
    'Party'
    __name__ = 'party.party'

    upi = fields.Function(fields.Char('UPI', help='Unique Party Identifier'),
                          'get_upi_display', searcher='search_upi')
    firstname = fields.Char('First name', states=_STATES, depends=_DEPENDS,
                            select=True)
    middlename = fields.Char('Middle Name', states=_STATES, depends=_DEPENDS,
                             help="Middle name or names of Patient")
    maiden_name = fields.Char(
        'Maiden Name',
        states={'invisible': Or(Not(In(Eval('marital_status'),
                                       ['m', 'c', 'w', 'd', 'x'])),
                                Equal(Eval('sex'), 'm'))}
    )
    mother_maiden_name = fields.Char("Mother's Maiden Name", states=_STATES,
                                     depends=_DEPENDS,
                                     help="Mother's Maiden Name")
    father_name = fields.Char("Father's Name", states=_STATES,
                              depends=_DEPENDS, help="Father's Name")
    sex_display = fields.Function(fields.Char('Sex'), 'get_sex_display')
    # gender vs sex: According to the AMA Manual of Style :
    # Gender refers to the psychological/societal aspects of being male
    # or female, sex refers specifically to the physical aspects.
    # Do not interchange

    alt_ids = fields.Function(fields.Char('Alternate IDs'), 'get_alt_ids',
                              searcher='search_alt_ids')
    medical_record_num = fields.Function(
        fields.Char('Medical Record Num.'),
        'get_alt_ids', searcher='search_alt_ids')
    suffix = fields.Selection([
        (None, ''),
        ('jr', 'Jr. - Junior'),
        ('sr', 'Sr. - Senior'),
        ('II', 'II - The Second'),
        ('III', 'III - The Third'),
        ], 'Suffix', states=_STATES, depends=_DEPENDS)
    marital_status_display = fields.Function(fields.Char('Marital Status'),
                                             'get_marital_status_display')
    relatives = fields.One2Many('party.relative', 'party', 'Relatives')
    reverse_relatives = fields.One2Many('party.relative', 'relative',
                                        'Related To', readonly=True)
    birth_country = fields.Many2One(
        'country.country', 'Country of Birth',
        states={'invisible': Not(Bool(Eval('is_person')))})
    birth_subdiv = fields.Many2One(
        'country.subdivision', 'Place of Birth',
        depends=['birth_country'],
        domain=[('country', '=', Eval('birth_country'))],
        states={'invisible': Not(Bool(Eval('is_person')))})

    birthplace = fields.Function(fields.Char('Place of Birth'),
                                 'get_rec_name')
    current_age = fields.Function(fields.Char('Age'), 'get_current_age')

    @classmethod
    def __setup__(cls):
        super(PartyPatient, cls).__setup__()

        # Error Message
        cls._error_messages.update({
            'future_dob_error':
            'Future Birth Error\n\nDate of birth cannot be in the future.',
            'unidentified_or_altid':
            'Indentity Verification Error\n\n'
            'Please enter an Alternate ID or check Undentified.\n'
        })

        # field behaviour modifications
        s = cls.name.states if cls.name.states else {}
        s['readonly'] = Bool(Eval('is_person'))
        cls.name.states = s.copy()
        cls.ref.states['readonly'] = True
        cls.lastname.select = True
        cls.sex.selection = [(None, '')] + SEX_OPTIONS
        cls.marital_status.selection = [(None, '')] + MARITAL_STATUSES
        cls.internal_user.states = {
            'invisible': Not(Bool(Eval('is_healthprof'))),
            'required': False,
        }
        cls.alternative_ids.states = {
            'invisible': Not(Eval('is_person', False))}

        # field label modifications
        cls.ref.string = "UPI"
        cls.insurance.string = "Insurance Plans"
        cls.alternative_ids.string = 'Alternate IDs'
        cls.dob.string = 'Date of Birth'

        # help text mods
        cls.ref.help = "Unique Party Indentifier"
        cls.alias.help = "Pet-name or other name by which party is known"
        cls.internal_user.help = ' '.join([
            'In order for this health professional to use '
            'the system, an internal user account must be assigned. '
            'This health professional will have a user account in this '
            'instance only.'])

    @classmethod
    def validate(cls, parties):
        super(PartyPatient, cls).validate(parties)
        for party in parties:
            party.check_dob()
            if is_not_synchro():
                party.check_party_warning()

    def check_dob(self):
        if self.dob and self.dob > date.today():
            self.raise_user_error('future_dob_error')

    def check_party_warning(self):
        '''validates that a party being entered as verified has an alt-id
        if there is no alt-id, then the party should be labeled as unidentified
        '''
        if (self.is_person and self.is_patient and
                len(self.alternative_ids) == 0 and not self.unidentified):
            self.raise_user_error('unidentified_or_altid')

    def get_rec_name(self, name):
        if name == 'birthplace':
            return ', '.join([x.name for x in
                             filter(None, [self.birth_country,
                                    self.birth_subdiv])])
        else:
            return self.name

    @staticmethod
    def default_alternative_identification():
        return True

    @staticmethod
    def default_unidentified():
        return False

    @staticmethod
    def default_birth_country():
        country, = Pool().get('country.country').search([('code', '=', 'JM')])
        return country.id

    @fields.depends('lastname', 'firstname', 'middlename')
    def on_change_with_name(self, *arg, **kwarg):
        namelist = []
        if self.lastname:
            namelist.append(''.join([self.lastname, ',']))
        if self.firstname:
            namelist.append(self.firstname)
        if self.middlename:
            namelist.append(self.middlename)
        return ' '.join(namelist)

    @classmethod
    def get_upi_display(cls, instances, name):
        return dict([(i.id,
                      'NN-%s' % i.ref if i.is_patient and
                      i.unidentified else i.ref)
                     for i in instances])

    @classmethod
    def search_upi(cls, field_name, clause):
        # TODO: Fix this to work for 'in' and 'like' clauses
        fld, operator, operand = clause
        if (isinstance(operand, six.string_types) and NNre.match(operand)):
            # searching for NN- records, so auto-append unidentified=True
            operand = u''.join(NNre.split(operand))
            if operand == u'%%':
                operand = '%'
            return ['AND', ('ref', operator, operand),
                    ('unidentified', '=', True)]
        else:
            return [replace_clause_column(clause, 'ref')]

    def get_sex_display(self, field_name):
        sex_dict = dict(SEX_OPTIONS)
        return sex_dict.get(self.sex, '')

    def get_alt_ids(self, field_name):
        here = ThisInstitution()
        id_type_map = dict(ALTERNATIVE_ID_TYPES)
        if (field_name == 'medical_record_num'):
            for altid in self.alternative_ids:
                if (altid.alternative_id_type == 'medical_record' and
                        (altid.issuing_institution and
                         altid.issuing_institution.id == here)):
                    return altid.code
            return '--'
        else:
            altids = []
            for altid in self.alternative_ids:
                if (altid.alternative_id_type == 'medical_record' and
                        altid.issuing_institution and
                        altid.issuing_institution.id == here):
                    continue
                else:
                    a_type = id_type_map.get(altid.alternative_id_type,
                                             altid.alternative_id_type)
                    if (altid.alternative_id_type == 'medical_record' and
                            altid.issuing_institution):
                        a_type = '{} MRN'.format(
                            altid.issuing_institution.name.name)
                    elif altid.alternative_id_type == 'medical_record':
                        a_type = 'Unknown MRN'
                    altids.append('{} {}'.format(a_type, altid.code))
            return '; '.join(altids)

    @classmethod
    def search_alt_ids(cls, field_name, clause):
        if field_name == 'medical_record_num':
            return ['AND', ('alternative_ids.alternative_id_type', '=',
                           'medical_record'),
                    ('alternative_ids.code',)+tuple(clause[1:])]
        else:
            return [
                'AND',
                ('alternative_ids.alternative_id_type', '!=', 'medical_record'),
                ('alternative_ids.code', clause[1], clause[2])]

    def get_marital_status_display(s, n):
        return make_selection_display()(s, 'marital_status')

    @classmethod
    def generate_puid(cls):
        # Add a default random string in the ref field.
        # The STRSIZE constant provides the length of the HIN
        # The format of the UPC is XXXNNNXXX
        STRSIZE = 9
        letters = ('ABCDEFGHJKLMNPRTUWXY')
        digits = '0123456789'
        # letters removed = IOQSVZ because they look too similar to numbers
        # or to other letters.
        hin = ''
        for x in range(STRSIZE):
            if (x < 3 or x > 5):
                hin = hin + random.choice(letters)
            else:
                hin = hin + random.choice(digits)
        return hin

    @classmethod
    def create(cls, vlist):
        Configuration = Pool().get('party.configuration')

        vlist = [x.copy() for x in vlist]
        for values in vlist:
            if not 'ref' in values or not values['ref']:
                values['ref'] = cls.generate_puid()
                if 'is_person' in values and not values['is_person']:
                    values['ref'] = 'NP-' + values['ref']

            if not values.get('code'):
                # Use the company name . Initially, use the name
                # since the company hasn't been created yet.
                institution_id = ThisInstitution()
                if institution_id:
                    institution = Pool().get(
                        'gnuhealth.institution')(institution_id)
                    suffix = institution.code
                else:
                    suffix = Transaction().context.get('company.rec_name')\
                        or values['name']
                values['code'] = '-'.join([str(x) for x in
                                          (uuid.uuid4(), suffix)])
            values.setdefault('addresses', None)
        return super(PartyPatient, cls).create(vlist)

    @classmethod
    def get_current_age(cls, instances, name):
        c = Transaction().cursor
        tbl = cls.__table__()
        qry = "\n".join([
            "SELECT a.id as id, "
            "CASE WHEN a.dob is Null THEN '--'::varchar "
            "ELSE regexp_replace(AGE(a.dob)::varchar, "
            "' ([ymd])[ayonthears ]+', '\\1 ', 'g') END as showage",
            "from " + str(tbl) + " as a", "where a.id in %s;"])
        qry_parm = tuple(map(int, instances))
        c.execute(qry, (qry_parm, ))
        outx = c.fetchall()
        outd = dict([x for x in outx])
        return outd
Ejemplo n.º 3
0
 def view_attributes(cls):
     return super(Product, cls).view_attributes() + [
         ('//page[@id="customers"]', 'states', {
             'invisible': Not(Bool(Eval('displayed_on_eshop')))
         })
     ]
Ejemplo n.º 4
0
class Forecast(Workflow, ModelSQL, ModelView):
    "Stock Forecast"
    __name__ = "stock.forecast"
    _rec_name = 'warehouse'
    warehouse = fields.Many2One('stock.location',
                                'Location',
                                required=True,
                                domain=[('type', '=', 'warehouse')],
                                states={
                                    'readonly':
                                    Or(Not(Equal(Eval('state'), 'draft')),
                                       Bool(Eval('lines', [0]))),
                                },
                                depends=['state'])
    destination = fields.Many2One('stock.location',
                                  'Destination',
                                  required=True,
                                  domain=[('type', 'in',
                                           ['customer', 'production'])],
                                  states=STATES,
                                  depends=DEPENDS)
    from_date = fields.Date('From Date',
                            required=True,
                            states=STATES,
                            depends=DEPENDS)
    to_date = fields.Date('To Date',
                          required=True,
                          states=STATES,
                          depends=DEPENDS)
    lines = fields.One2Many('stock.forecast.line',
                            'forecast',
                            'Lines',
                            states=STATES,
                            depends=DEPENDS)
    company = fields.Many2One('company.company',
                              'Company',
                              required=True,
                              states={
                                  'readonly':
                                  Or(Not(Equal(Eval('state'), 'draft')),
                                     Bool(Eval('lines', [0]))),
                              },
                              depends=['state'])
    state = fields.Selection([
        ('draft', 'Draft'),
        ('done', 'Done'),
        ('cancel', 'Cancel'),
    ],
                             'State',
                             readonly=True,
                             select=True)
    active = fields.Function(fields.Boolean('Active'),
                             'get_active',
                             searcher='search_active')

    @classmethod
    def __setup__(cls):
        super(Forecast, cls).__setup__()
        t = cls.__table__()
        cls._sql_constraints += [
            ('check_from_to_date', Check(t, t.to_date >= t.from_date),
             '"To Date" must be greater than "From Date"'),
        ]
        cls._error_messages.update({
            'date_overlap': ('Forecast "%(first)s" overlaps with dates '
                             'of forecast "%(second)s" in the same location.'),
            'delete_cancel': ('Forecast "%s" must be cancelled before '
                              'deletion.'),
        })
        cls._order.insert(0, ('from_date', 'DESC'))
        cls._order.insert(1, ('warehouse', 'ASC'))
        cls._transitions |= set((
            ('draft', 'done'),
            ('draft', 'cancel'),
            ('done', 'draft'),
            ('cancel', 'draft'),
        ))
        cls._buttons.update({
            'cancel': {
                'invisible': Eval('state') != 'draft',
            },
            'draft': {
                'invisible': Eval('state') == 'draft',
            },
            'confirm': {
                'invisible': Eval('state') != 'draft',
            },
            'complete': {
                'readonly': Eval('state') != 'draft',
            },
        })

    @classmethod
    def __register__(cls, module_name):
        Location = Pool().get('stock.location')
        TableHandler = backend.get('TableHandler')
        cursor = Transaction().cursor
        sql_table = cls.__table__()

        table = TableHandler(cursor, cls, module_name)
        migrate_warehouse = (not table.column_exist('warehouse')
                             and table.column_exist('location'))

        super(Forecast, cls).__register__(module_name)

        # Add index on create_date
        table = TableHandler(cursor, cls, module_name)
        table.index_action('create_date', action='add')

        if migrate_warehouse:
            location2warehouse = {}

            def find_warehouse(location):
                if location.type == 'warehouse':
                    return location.id
                elif location.parent:
                    return find_warehouse(location.parent)

            cursor.execute(*sql_table.select(sql_table.id, sql_table.location))
            for forecast_id, location_id in cursor.fetchall():
                warehouse_id = location_id  # default fallback
                if location_id in location2warehouse:
                    warehouse_id = location2warehouse[location_id]
                else:
                    location = Location(location_id)
                    warehouse_id = find_warehouse(location) or location_id
                    location2warehouse[location_id] = warehouse_id
                cursor.execute(
                    *sql_table.update(columns=[sql_table.warehouse],
                                      values=[warehouse_id],
                                      where=sql_table.id == forecast_id))
            table.not_null_action('warehouse',
                                  action=cls.warehouse.required and 'add'
                                  or 'remove')
            table.drop_column('location', True)

        # Migration from 2.0 delete stock moves
        forecasts = cls.search([])
        cls.delete_moves(forecasts)

    @staticmethod
    def default_state():
        return 'draft'

    @classmethod
    def default_destination(cls):
        Location = Pool().get('stock.location')
        locations = Location.search(cls.destination.domain)
        if len(locations) == 1:
            return locations[0].id

    @staticmethod
    def default_company():
        return Transaction().context.get('company')

    def get_active(self, name):
        pool = Pool()
        Date = pool.get('ir.date')
        return self.to_date >= Date.today()

    @classmethod
    def search_active(cls, name, clause):
        pool = Pool()
        Date = pool.get('ir.date')

        today = Date.today()
        operators = {
            '=': '>=',
            '!=': '<',
        }
        reverse = {
            '=': '!=',
            '!=': '=',
        }
        if clause[1] in operators:
            if clause[2]:
                return [('to_date', operators[clause[1]], today)]
            else:
                return [('to_date', operators[reverse[clause[1]]], today)]
        else:
            return []

    @classmethod
    def validate(cls, forecasts):
        super(Forecast, cls).validate(forecasts)
        for forecast in forecasts:
            forecast.check_date_overlap()

    def check_date_overlap(self):
        cursor = Transaction().cursor
        if self.state != 'done':
            return
        forcast = self.__table__()
        cursor.execute(
            *forcast.select(forcast.id,
                            where=(((forcast.from_date <= self.from_date)
                                    & (forcast.to_date >= self.from_date))
                                   | ((forcast.from_date <= self.to_date)
                                      & (forcast.to_date >= self.to_date))
                                   | ((forcast.from_date >= self.from_date)
                                      & (forcast.to_date <= self.to_date)))
                            & (forcast.warehouse == self.warehouse.id)
                            & (forcast.destination == self.destination.id)
                            & (forcast.company == self.company.id)
                            & (forcast.id != self.id)))
        forecast_id = cursor.fetchone()
        if forecast_id:
            second = self.__class__(forecast_id[0])
            self.raise_user_error('date_overlap', {
                'first': self.rec_name,
                'second': second.rec_name,
            })

    @classmethod
    def delete(self, forecasts):
        # Cancel before delete
        self.cancel(forecasts)
        for forecast in forecasts:
            if forecast.state != 'cancel':
                self.raise_user_error('delete_cancel', forecast.rec_name)
        super(Forecast, self).delete(forecasts)

    @classmethod
    @ModelView.button
    @Workflow.transition('draft')
    def draft(cls, forecasts):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('done')
    def confirm(cls, forecasts):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('cancel')
    def cancel(cls, forecasts):
        pass

    @classmethod
    @ModelView.button_action('stock_forecast.wizard_forecast_complete')
    def complete(cls, forecasts):
        pass

    @staticmethod
    def create_moves(forecasts):
        'Create stock moves for the forecast ids'
        for forecast in forecasts:
            if forecast.state == 'done':
                for line in forecast.lines:
                    line.create_moves()

    @staticmethod
    def delete_moves(forecasts):
        'Delete stock moves for the forecast ids'
        Line = Pool().get('stock.forecast.line')
        Line.delete_moves([l for f in forecasts for l in f.lines])

    @classmethod
    def copy(cls, forecasts, default=None):
        Line = Pool().get('stock.forecast.line')

        if default is None:
            default = {}
        default = default.copy()
        default['lines'] = None

        new_forecasts = []
        for forecast in forecasts:
            new_forecast, = super(Forecast, cls).copy([forecast],
                                                      default=default)
            Line.copy([x for x in forecast.lines],
                      default={
                          'forecast': new_forecast.id,
                      })
            new_forecasts.append(new_forecast)
        return new_forecasts
Ejemplo n.º 5
0
class SaleOpportunityHistory(ModelSQL, ModelView):
    'Sale Opportunity History'
    __name__ = 'sale.opportunity.history'
    date = fields.DateTime('Change Date')
    opportunity = fields.Many2One('sale.opportunity', 'Sale Opportunity')
    user = fields.Many2One('res.user', 'User')
    party = fields.Many2One('party.party', 'Party', datetime_field='date')
    address = fields.Many2One('party.address', 'Address',
            datetime_field='date')
    company = fields.Many2One('company.company', 'Company',
            datetime_field='date')
    employee = fields.Many2One('company.employee', 'Employee',
            datetime_field='date')
    start_date = fields.Date('Start Date')
    end_date = fields.Date('End Date', states={
        'invisible': Not(In(Eval('state'),
            ['converted', 'cancelled', 'lost'])),
    }, depends=['state'])
    description = fields.Char('Description')
    comment = fields.Text('Comment')
    lines = fields.Function(fields.One2Many('sale.opportunity.line', None,
            'Lines', datetime_field='date'), 'get_lines')
    amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
        depends=['currency_digits'])
    currency = fields.Function(fields.Many2One('currency.currency',
            'Currency'), 'get_currency')
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
        'get_currency_digits')
    state = fields.Selection(STATES, 'State')
    probability = fields.Integer('Conversion Probability')
    lost_reason = fields.Text('Reason for loss', states={
        'invisible': Not(Equal(Eval('state'), 'lost')),
    }, depends=['state'])

    @classmethod
    def __setup__(cls):
        super(SaleOpportunityHistory, cls).__setup__()
        cls._order.insert(0, ('date', 'DESC'))

    def get_currency(self, name):
        return self.company.currency.id

    def get_currency_digits(self, name):
        return self.company.currency.digits

    @classmethod
    def table_query(cls):
        Opportunity = Pool().get('sale.opportunity')
        opportunity_history = Opportunity.__table_history__()
        columns = [
            Min(Column(opportunity_history, '__id')).as_('id'),
            opportunity_history.id.as_('opportunity'),
            Min(Coalesce(opportunity_history.write_date,
                    opportunity_history.create_date)).as_('date'),
            Coalesce(opportunity_history.write_uid,
                opportunity_history.create_uid).as_('user'),
            ]
        group_by = [
            opportunity_history.id,
            Coalesce(opportunity_history.write_uid,
                opportunity_history.create_uid),
            ]
        for name, field in cls._fields.iteritems():
            if name in ('id', 'opportunity', 'date', 'user'):
                continue
            if hasattr(field, 'set'):
                continue
            column = Column(opportunity_history, name)
            columns.append(column.as_(name))
            group_by.append(column)

        return opportunity_history.select(*columns, group_by=group_by)

    def get_lines(self, name):
        Line = Pool().get('sale.opportunity.line')
        # We will always have only one id per call due to datetime_field
        lines = Line.search([
                ('opportunity', '=', self.opportunity.id),
                ])
        return [l.id for l in lines]

    @classmethod
    def read(cls, ids, fields_names=None):
        res = super(SaleOpportunityHistory, cls).read(ids,
            fields_names=fields_names)

        # Remove microsecond from timestamp
        for values in res:
            if 'date' in values:
                if isinstance(values['date'], basestring):
                    values['date'] = datetime.datetime(
                        *time.strptime(values['date'],
                            '%Y-%m-%d %H:%M:%S.%f')[:6])
                values['date'] = values['date'].replace(microsecond=0)
        return res
Ejemplo n.º 6
0
class Recibo(Workflow, ModelSQL, ModelView):
    "cooperative.partner.recibo"
    __name__ = "cooperative.partner.recibo"
    date = fields.Date('Fecha',
                       states={'readonly': (Eval('state') != 'draft')},
                       required=True)
    amount = fields.Numeric('Monto',
                            digits=(16, 2),
                            states={'readonly': (Eval('state') != 'draft')},
                            required=True)
    partner = fields.Many2One('cooperative.partner',
                              'Socio',
                              required=True,
                              states={'readonly': (Eval('state') != 'draft')})
    state = fields.Selection([
        ('draft', 'Borrador'),
        ('confirmed', 'Confirmado'),
        ('paid', 'Pagado'),
        ('cancel', 'Cancelado'),
    ],
                             'State',
                             readonly=True)
    number = fields.Char('Numero', size=None, readonly=True, select=True)

    description = fields.Char('Descripcion',
                              size=None,
                              states=_STATES,
                              depends=_DEPENDS)

    ## Integrando con asientos
    party = fields.Function(
        fields.Many2One('party.party',
                        'Entidad',
                        required=True,
                        states=_STATES,
                        depends=_DEPENDS,
                        on_change_with=['partner']), 'on_change_with_party')
    #company = fields.Many2One('company.company', 'Company', required=True,
    #    states=_STATES, select=True, domain=[
    #        ('id', If(Eval('context', {}).contains('company'), '=', '!='),
    #            Eval('context', {}).get('company', -1)),
    #        ],
    #    depends=_DEPENDS)

    company = fields.Many2One('company.company',
                              'Coooperativa',
                              required=True,
                              states=_STATES,
                              select=True,
                              depends=_DEPENDS)

    accounting_date = fields.Date('Fecha de Contabilizacion',
                                  states=_STATES,
                                  depends=_DEPENDS)
    confirmed_move = fields.Many2One('account.move',
                                     'Asiento de Confirmacion',
                                     readonly=True)
    paid_move = fields.Many2One('account.move',
                                'Asiento de Pago',
                                readonly=True,
                                states={
                                    'invisible':
                                    Eval('state').in_(['draft', 'confirmed']),
                                })
    journal = fields.Many2One('account.journal',
                              'Diario',
                              states=_STATES,
                              depends=_DEPENDS)
    currency = fields.Many2One('currency.currency',
                               'Moneda',
                               required=True,
                               states={
                                   'readonly':
                                   ((Eval('state') != 'draft')
                                    | (Eval('lines') & Eval('currency'))),
                               },
                               depends=['state', 'lines'])

    periodo_liquidado = fields.Char('Periodo Liquidado', required=True)
    fecha_pago = fields.Date('Fecha de Pago', required=True)

    #Para concepto de Monotributo
    pago_monotributo = fields.Boolean('Pago de monotributo')
    valor_monotributo = fields.Numeric(
        'Valor del monotributo',
        digits=(16, 2),
        states={'invisible': Not(Bool(Eval('pago_monotributo')))})
    mes_monotributo = fields.Selection(
        [('', ''), ('Enero', 'Enero'), ('Febrero', 'Febrero'),
         ('Marzo', 'Marzo'), ('Abril', 'Abril'), ('Mayo', 'Mayo'),
         ('Junio', 'Junio'), ('Julio', 'Julio'), ('Agosto', 'Agosto'),
         ('Septiembre', 'Septiembre'), ('Octubre', 'Octubre'),
         ('Noviembre', 'Noviembre'), ('Diciembre', 'Diciembre')],
        'Mes del Monotributo',
        states={'invisible': Not(Bool(Eval('pago_monotributo')))},
    )

    #Para pago de cuotas
    cobro_cuota = fields.Boolean('Cobro de cuota')
    valor_cuota = fields.Numeric(
        'Valor de la cuota',
        digits=(16, 2),
        states={'invisible': Not(Bool(Eval('cobro_cuota')))})
    mes_cuota = fields.Selection(
        [('', ''), ('Enero', 'Enero'), ('Febrero', 'Febrero'),
         ('Marzo', 'Marzo'), ('Abril', 'Abril'), ('Mayo', 'Mayo'),
         ('Junio', 'Junio'), ('Julio', 'Julio'), ('Agosto', 'Agosto'),
         ('Septiembre', 'Septiembre'), ('Octubre', 'Octubre'),
         ('Noviembre', 'Noviembre'), ('Diciembre', 'Diciembre')],
        'Mes de la Cuota',
        states={'invisible': Not(Bool(Eval('cobro_cuota')))},
    )

    #Para otros conceptos (AUH, etc)
    pago_otros = fields.Boolean('Pago de adicional')
    concepto_otros = fields.Char(
        'Nombre del concepto adicional',
        states={'invisible': Not(Bool(Eval('pago_otros')))})
    valor_otros = fields.Numeric(
        'Valor del concepto adicional',
        digits=(16, 2),
        states={'invisible': Not(Bool(Eval('pago_otros')))})

    #Otros conceptos
    lineas_conceptos_recibo = fields.One2Many(
        'cooperative.partner.recibo.lineaconceptorecibo', 'recibo',
        'Conceptos a incluir en el recibo')

    total = fields.Function(
        fields.Numeric('Total',
                       digits=(16, 2),
                       on_change_with=[
                           'amount', 'pago_monotributo', 'cobro_cuota',
                           'valor_cuota', 'valor_monotributo', 'total',
                           'lineas_conceptos_recibo'
                       ]), 'on_change_with_total')

    total_en_letras = fields.Function(fields.Char('Total en letras'),
                                      'get_sing_number')

    @classmethod
    def __setup__(cls):
        super(Recibo, cls).__setup__()
        cls._transitions |= set((
            ('draft', 'confirmed'),
            ('draft', 'cancel'),
            ('confirmed', 'draft'),
            ('confirmed', 'paid'),
            ('confirmed', 'cancel'),
            ('cancel', 'draft'),
        ))

        cls._buttons.update({
            'cancel': {
                'invisible': ~Eval('state').in_(['draft']),
            },
            'draft': {
                'invisible': ~Eval('state').in_(['cancel']),
            },
            'paid': {
                'invisible': ~Eval('state').in_(['confirmed']),
            },
            'confirmed': {
                'invisible': ~Eval('state').in_(['draft']),
            },
        })

    @classmethod
    @ModelView.button
    @Workflow.transition('draft')
    def draft(cls, recibos):
        Move = Pool().get('account.move')

        moves = []
        for recibo in recibos:

            if recibo.confirmed_move:
                moves.append(recibo.confirmed_move)
        if moves:
            with Transaction().set_user(0, set_context=True):
                Move.delete(moves)

    @classmethod
    @ModelView.button
    @Workflow.transition('confirmed')
    def confirmed(cls, recibos):
        Move = Pool().get('account.move')

        moves = []
        for recibo in recibos:
            recibo.journal = Pool().get('account.journal').search([
                ('code', '=', 'EXP')
            ])[0]
            recibo.save()
            recibo.set_number()
            moves.append(recibo.create_confirmed_move())

        cls.write(recibos, {
            'state': 'confirmed',
        })
        Move.post(moves)

    @classmethod
    @ModelView.button
    @Workflow.transition('paid')
    def paid(cls, recibos):
        Move = Pool().get('account.move')

        moves = []
        for recibo in recibos:
            moves.append(recibo.create_paid_move())

        cls.write(recibos, {
            'state': 'paid',
        })
        Move.post(moves)

    @classmethod
    @ModelView.button
    @Workflow.transition('cancel')
    def cancel(cls, recibos):
        cls.write(recibos, {
            'state': 'cancel',
        })

    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def default_description():
        return 'Retornos a cuenta de excedentes'

    @staticmethod
    def default_date():
        Date_ = Pool().get('ir.date')
        return Date_.today()

    @staticmethod
    def default_fecha_pago():
        Date_ = Pool().get('ir.date')
        return Date_.today()

    @staticmethod
    def default_amount():
        return Decimal(0)

    @staticmethod
    def default_valor_cuota():
        return Decimal(0)

    @staticmethod
    def default_valor_monotributo():
        return Decimal(0)

    @staticmethod
    def default_valor_otros():
        return Decimal(0)

    @staticmethod
    def default_company():
        return Transaction().context.get('company')

    @staticmethod
    def default_currency():
        Company = Pool().get('company.company')
        if Transaction().context.get('company'):
            company = Company(Transaction().context['company'])
            return company.currency.id

    def on_change_with_party(self, name=None):
        if self.partner:
            return self.partner.party.id

    def on_change_with_total(self, name=None):
        if self.pago_monotributo:
            total = Decimal(self.amount) + Decimal(self.valor_monotributo)
        else:
            total = Decimal(self.amount)
        if self.cobro_cuota:
            total -= Decimal(self.valor_cuota)
        if self.lineas_conceptos_recibo:
            for linea in self.lineas_conceptos_recibo:
                if linea.valor:
                    total += Decimal(linea.valor)
        #if self.pago_otros:
        #    total += Decimal(self.valor_otros)

        return total

    def set_number(self):
        '''
        Set number to the receipt
        '''
        pool = Pool()
        FiscalYear = pool.get('account.fiscalyear')
        Date = pool.get('ir.date')
        Sequence = pool.get('ir.sequence')

        if self.number:
            return

        accounting_date = self.accounting_date or self.date
        fiscalyear_id = FiscalYear.find(self.company.id, date=accounting_date)
        fiscalyear = FiscalYear(fiscalyear_id)
        sequence = fiscalyear.get_sequence('receipt')

        #if not sequence:
        #    self.raise_user_error('no_invoice_sequence', {
        #            'invoice': self.rec_name,
        #            'period': period.rec_name,
        #            })
        with Transaction().set_context(date=self.date or Date.today()):
            number = Sequence.get_id(sequence.id)
            vals = {'number': number}

        self.write([self], vals)

    def _get_move_line(self, date, amount, account_id):
        '''
        Return move line
        '''
        Currency = Pool().get('currency.currency')
        res = {}
        if self.currency.id != self.company.currency.id:
            with Transaction().set_context(date=self.date):
                res['amount_second_currency'] = Currency.compute(
                    self.company.currency, amount, self.currency)
            res['amount_second_currency'] = abs(res['amount_second_currency'])
            res['second_currency'] = self.currency.id
        else:
            res['amount_second_currency'] = Decimal('0.0')
            res['second_currency'] = None
        if amount >= Decimal('0.0'):
            res['debit'] = Decimal('0.0')
            res['credit'] = amount
        else:
            res['debit'] = -amount
            res['credit'] = Decimal('0.0')
        res['account'] = account_id
        res['maturity_date'] = date
        res['description'] = self.description
        res['party'] = self.party.id
        return res

    def create_move(self, move_lines):

        pool = Pool()
        Move = pool.get('account.move')
        Period = pool.get('account.period')

        accounting_date = self.accounting_date or self.date
        period_id = Period.find(self.company.id, date=accounting_date)

        move, = Move.create([{
            'journal': self.journal.id,
            'period': period_id,
            'date': self.date,
            #            'origin': str(self),
            'lines': [('create', move_lines)],
        }])
        return move

    def create_confirmed_move(self):
        '''
        Create account move for the receipt and return the created move
        '''
        pool = Pool()
        Date = pool.get('ir.date')

        move_lines = []

        val = self._get_move_line(Date.today(), self.amount,
                                  self.party.account_payable.id)
        move_lines.append(val)
        # issue #4461
        # En vez de usar la cuenta "a cobrar" del party, deberia ser la
        # cuenta Retornos Asociados (5242) siempre fija, que esta seteada como
        # Expense (Gasto).
        account_receivable = self.party.account_receivable.search([
            ('rec_name', 'like', '%5242%')
        ])[0]
        val = self._get_move_line(Date.today(), -self.amount,
                                  account_receivable.id)
        move_lines.append(val)

        move = self.create_move(move_lines)

        self.write([self], {
            'confirmed_move': move.id,
        })
        return move

    def create_paid_move(self):
        '''
        Create account move for the receipt and return the created move
        '''
        pool = Pool()
        Date = pool.get('ir.date')

        move_lines = []

        val = self._get_move_line(Date.today(), self.amount,
                                  self.journal.credit_account.id)
        move_lines.append(val)
        val = self._get_move_line(Date.today(), -self.amount,
                                  self.party.account_payable.id)
        move_lines.append(val)

        move = self.create_move(move_lines)

        self.write([self], {
            'paid_move': move.id,
        })
        return move

    def get_sing_number(self, name=None):
        "Convert numbers in its equivalent string text representation in spanish"
        from singing_girl import Singer
        singer = Singer()
        return singer.sing(self.total)
Ejemplo n.º 7
0
 def __setup__(cls):
     super(Template, cls).__setup__()
     cls.products.states['readonly'] = Not(Eval('variante', False))
Ejemplo n.º 8
0
class notacreditoStart(ModelView):
    'notacredito Start'
    __name__ = 'sigcoop_wizard_ventas.notacredito.start'
    tipoemi = fields.Selection([
        ('otro','Otro'),
        ('porid','Por numero de ID'),
        ('portarifa', 'Por Tarifa')
        ], 'Tipo de Emision', required=True)

    desdeid = fields.Integer('Desde ID',
        states={'required': Equal(Eval('tipoemi'),'porid')})
 
    hastaid = fields.Integer('Hasta ID',
        states={'required': Equal(Eval('tipoemi'),'porid')})

    servicio = fields.Many2One('product.category', 'Tipo de servicio', required=True,
        on_change=['servicio', 'mismocesp'])
    servicio_char = fields.Char('servicio_char')

    periodo = fields.Many2One('sigcoop_periodo.periodo', 'Periodo',
        states={'required': Equal(Eval('tipoemi'),'portarifa')},
        domain=[ ('category', '=', Eval('servicio'))])

    lista_precios = fields.Many2One('product.price_list', 'Tarifa',
                states={'required': Equal(Eval('tipoemi'),'portarifa')},
                domain = [
                    ('servicio', '=', Eval('servicio')),
                    ('tarifa_oculta', '=', False )])

    consumo_cero = fields.Boolean('NC de Facturas con consumo cero', select=False,
                    states={'readonly': Not(Equal(Eval('servicio_char'),'Energia') |
                            Equal(Eval('servicio_char'),'Agua'))
                    })

    mismocesp = fields.Boolean('Usar CESP y Fecha de la factura', select=False,
        states={'readonly': Not(Equal(Eval('servicio_char'),'Energia') |
                            Equal(Eval('servicio_char'),'Agua'))
    })
    numerocesp = fields.Many2One('account.cesp', 'C.E.S.P.',
        states={'required': Equal(Eval('servicio_char'),'Energia') |
                            Equal(Eval('servicio_char'),'Agua'),
                'readonly': Or(Bool(Eval('mismocesp')), Not(Equal(Eval('servicio_char'),'Energia') |
                            Equal(Eval('servicio_char'),'Agua'))),
    })

    fecha_nc = fields.Date('Fecha de Nota de Credito',
        states={'required': Equal(Eval('servicio_char'),'Energia') |
                            Equal(Eval('servicio_char'),'Agua'),
                'readonly': Bool(Eval('mismocesp')),
    })


    def on_change_servicio(self):
        serv=''
        ret={}
        if (self.servicio and self.servicio.name):
            serv=self.servicio.name
        if (self.servicio.name!='Energia') or (self.servicio.name!='Agua'):
            ret['mismocesp']=False

        ret['servicio_char']=serv
 
        return ret
Ejemplo n.º 9
0
 def view_attributes(cls):
     return super().view_attributes() + [
         ('//group[@id="tas"]', 'states', {
             'invisible': Not(Bool(Equal(Eval('type'), 'tas'))),
         }),
     ]
class Invoice(metaclass=PoolMeta):
    __name__ = 'account.invoice'
    payment_type_kind = fields.Function(fields.Selection(KINDS,
            'Kind of payment type',
            states={
                'invisible': True,
                },
            ),
        'on_change_with_payment_type_kind')
    payment_type = fields.Many2One('account.payment.type', 'Payment Type',
        domain=[
            ('kind', 'in', ['both', Eval('payment_type_kind')]),
            ],
        states={
            'readonly': Not(Bool(Eval('state').in_(['draft', 'validated']))),
            },
        depends=['payment_type_kind', 'state'])

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls.payment_direct_debit.states = {
            'invisible': True,
        }

    @fields.depends('type', 'untaxed_amount', 'lines')
    def on_change_with_payment_type_kind(self, name=None):
        if self.untaxed_amount:
            if self.type == 'out':
                if self.untaxed_amount >= ZERO:
                    return 'receivable'
                else:
                    return 'payable'
            elif self.type == 'in':
                if self.untaxed_amount >= ZERO:
                    return 'payable'
                else:
                    return 'receivable'
        return 'receivable' if self.type == 'out' else 'payable'

    @fields.depends('party', 'company', 'type', 'untaxed_amount', 'lines')
    def on_change_with_payment_type(self, name=None):
        if (hasattr(self, 'payment_type') and self.payment_type
                and self.payment_type.kind == 'both'):
            return self.payment_type.id

        kind = self.on_change_with_payment_type_kind()
        if (hasattr(self, 'payment_type') and self.payment_type
                and self.payment_type.kind == kind):
            return self.payment_type.id

        if not self.untaxed_amount:
            return (self.payment_type.id
                if (hasattr(self, 'payment_type') and self.payment_type)
                else None)

        for party in [
                self.party,
                self.company.party if self.company else None]:
            if not party:
                continue

            if self.type == 'out':
                if kind == 'receivable':
                    name = 'customer_payment_type'
                else:
                    name = 'supplier_payment_type'
            elif self.type == 'in':
                if kind == 'payable':
                    name = 'supplier_payment_type'
                else:
                    name = 'customer_payment_type'

            payment_type = getattr(party, name)
            if payment_type:
                return payment_type.id
        return None

    def _get_move_line(self, date, amount):
        line = super(Invoice, self)._get_move_line(date, amount)
        if self.payment_type:
            line.payment_type = self.payment_type
        return line
Ejemplo n.º 11
0
class Forecast(Workflow, ModelSQL, ModelView):
    "Stock Forecast"
    __name__ = "stock.forecast"
    warehouse = fields.Many2One(
        'stock.location', 'Location', required=True,
        domain=[('type', '=', 'warehouse')], states={
            'readonly': Or(Not(Equal(Eval('state'), 'draft')),
                Bool(Eval('lines', [0]))),
            },
        depends=['state'])
    destination = fields.Many2One(
        'stock.location', 'Destination', required=True,
        domain=[('type', 'in', ['customer', 'production'])], states=STATES,
        depends=DEPENDS)
    from_date = fields.Date('From Date', required=True, states=STATES,
        depends=DEPENDS)
    to_date = fields.Date('To Date', required=True, states=STATES,
        depends=DEPENDS)
    lines = fields.One2Many(
        'stock.forecast.line', 'forecast', 'Lines', states=STATES,
        depends=DEPENDS)
    company = fields.Many2One(
        'company.company', 'Company', required=True, states={
            'readonly': Or(Not(Equal(Eval('state'), 'draft')),
                Bool(Eval('lines', [0]))),
            },
        depends=['state'])
    state = fields.Selection(
        FORECAST_STATES, 'State', readonly=True, select=True)
    active = fields.Function(fields.Boolean('Active'),
        'get_active', searcher='search_active')

    @classmethod
    def __setup__(cls):
        super(Forecast, cls).__setup__()
        t = cls.__table__()
        cls._sql_constraints += [
            ('check_from_to_date', Check(t, t.to_date >= t.from_date),
                '"To Date" must be greater than "From Date"'),
            ]
        cls._error_messages.update({
                'date_overlap': ('Forecast "%(first)s" overlaps with dates '
                    'of forecast "%(second)s" in the same location.'),
                'delete_cancel': ('Forecast "%s" must be cancelled before '
                    'deletion.'),
                })
        cls._order.insert(0, ('from_date', 'DESC'))
        cls._order.insert(1, ('warehouse', 'ASC'))
        cls._transitions |= set((
                ('draft', 'done'),
                ('draft', 'cancel'),
                ('done', 'draft'),
                ('cancel', 'draft'),
                ))
        cls._buttons.update({
                'cancel': {
                    'invisible': Eval('state') != 'draft',
                    'depends': ['state'],
                    },
                'draft': {
                    'invisible': Eval('state') == 'draft',
                    'depends': ['state'],
                    },
                'confirm': {
                    'invisible': Eval('state') != 'draft',
                    'depends': ['state'],
                    },
                'complete': {
                    'readonly': Eval('state') != 'draft',
                    'depends': ['state'],
                    },
                })
        cls._active_field = 'active'

    @classmethod
    def __register__(cls, module_name):
        super(Forecast, cls).__register__(module_name)

        # Add index on create_date
        table = cls.__table_handler__(module_name)
        table.index_action('create_date', action='add')

    @staticmethod
    def default_state():
        return 'draft'

    @classmethod
    def default_destination(cls):
        Location = Pool().get('stock.location')
        locations = Location.search(cls.destination.domain)
        if len(locations) == 1:
            return locations[0].id

    @staticmethod
    def default_company():
        return Transaction().context.get('company')

    def get_active(self, name):
        pool = Pool()
        Date = pool.get('ir.date')
        return self.to_date >= Date.today()

    @classmethod
    def search_active(cls, name, clause):
        pool = Pool()
        Date = pool.get('ir.date')

        today = Date.today()
        operators = {
            '=': '>=',
            '!=': '<',
            }
        reverse = {
            '=': '!=',
            '!=': '=',
            }
        if clause[1] in operators:
            if clause[2]:
                return [('to_date', operators[clause[1]], today)]
            else:
                return [('to_date', operators[reverse[clause[1]]], today)]
        else:
            return []

    def get_rec_name(self, name):
        return self.warehouse.rec_name

    @classmethod
    def search_rec_name(cls, name, clause):
        return [('warehouse.rec_name',) + tuple(clause[1:])]

    @classmethod
    def validate(cls, forecasts):
        super(Forecast, cls).validate(forecasts)
        for forecast in forecasts:
            forecast.check_date_overlap()

    def check_date_overlap(self):
        if self.state != 'done':
            return
        transaction = Transaction()
        connection = transaction.connection
        transaction.database.lock(connection, self._table)
        forcast = self.__table__()
        cursor = connection.cursor()
        cursor.execute(*forcast.select(forcast.id,
                where=(((forcast.from_date <= self.from_date)
                        & (forcast.to_date >= self.from_date))
                    | ((forcast.from_date <= self.to_date)
                        & (forcast.to_date >= self.to_date))
                    | ((forcast.from_date >= self.from_date)
                        & (forcast.to_date <= self.to_date)))
                & (forcast.warehouse == self.warehouse.id)
                & (forcast.destination == self.destination.id)
                & (forcast.company == self.company.id)
                & (forcast.id != self.id)))
        forecast_id = cursor.fetchone()
        if forecast_id:
            second = self.__class__(forecast_id[0])
            self.raise_user_error('date_overlap', {
                    'first': self.rec_name,
                    'second': second.rec_name,
                    })

    @classmethod
    def delete(cls, forecasts):
        # Cancel before delete
        cls.cancel(forecasts)
        for forecast in forecasts:
            if forecast.state != 'cancel':
                cls.raise_user_error('delete_cancel', forecast.rec_name)
        super(Forecast, cls).delete(forecasts)

    @classmethod
    @ModelView.button
    @Workflow.transition('draft')
    def draft(cls, forecasts):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('done')
    def confirm(cls, forecasts):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('cancel')
    def cancel(cls, forecasts):
        pass

    @classmethod
    @ModelView.button_action('stock_forecast.wizard_forecast_complete')
    def complete(cls, forecasts):
        pass

    @staticmethod
    def create_moves(forecasts):
        'Create stock moves for the forecast ids'
        pool = Pool()
        Line = pool.get('stock.forecast.line')
        to_save = []
        for forecast in forecasts:
            if forecast.state == 'done':
                for line in forecast.lines:
                    line.moves += tuple(line.get_moves())
                    to_save.append(line)
        Line.save(to_save)

    @staticmethod
    def delete_moves(forecasts):
        'Delete stock moves for the forecast ids'
        Line = Pool().get('stock.forecast.line')
        Line.delete_moves([l for f in forecasts for l in f.lines])
Ejemplo n.º 12
0
class Sale:
    "Sale"
    __name__ = 'sale.sale'

    is_international_shipping = fields.Function(
        fields.Boolean("Is International Shipping"),
        'on_change_with_is_international_shipping')

    weight = fields.Function(
        fields.Float(
            "Weight",
            digits=(16, Eval('weight_digits', 2)),
            depends=['weight_digits'],
        ), 'get_weight')

    weight_uom = fields.Function(fields.Many2One('product.uom', 'Weight UOM'),
                                 'get_weight_uom')
    weight_digits = fields.Function(fields.Integer('Weight Digits'),
                                    'on_change_with_weight_digits')

    available_carrier_services = fields.Function(
        fields.One2Many("carrier.service", None, 'Available Carrier Services'),
        getter="on_change_with_available_carrier_services")
    carrier_service = fields.Many2One(
        "carrier.service",
        "Carrier Service",
        domain=[('id', 'in', Eval('available_carrier_services'))],
        states={
            "invisible": Not(Bool(Eval('carrier'))),
            "readonly": Eval('state') != 'draft',
        },
        depends=['carrier', 'available_carrier_services', 'state'])
    carrier_cost_method = fields.Function(
        fields.Char('Carrier Cost Method'),
        "on_change_with_carrier_cost_method")

    @classmethod
    @ModelView.button_action('shipping.wizard_sale_apply_shipping')
    def apply_shipping(cls, sales):
        pass

    @fields.depends("carrier")
    def on_change_with_carrier_cost_method(self, name=None):
        if self.carrier:
            return self.carrier.carrier_cost_method

    def on_change_lines(self):
        """Pass a flag in context which indicates the get_sale_price method
        of carrier not to calculate cost on each line change
        """
        with Transaction().set_context({'ignore_carrier_computation': True}):
            super(Sale, self).on_change_lines()

    @fields.depends("carrier")
    def on_change_with_available_carrier_services(self, name=None):
        if self.carrier:
            return map(int, self.carrier.services)
        return []

    @classmethod
    def __setup__(cls):
        super(Sale, cls).__setup__()
        cls._error_messages.update({
            'warehouse_address_missing':
            'Warehouse address is missing',
        })
        cls._buttons.update({
            'apply_shipping': {
                "invisible":
                ~Eval('state').in_(['draft', 'quotation', 'confirmed'])
            }
        })
        cls.__rpc__.update({'apply_shipping_rate': RPC(instantiate=0)})

    @fields.depends('weight_uom')
    def on_change_with_weight_digits(self, name=None):
        if self.weight_uom:
            return self.weight_uom.digits
        return 2

    def get_weight_uom(self, name):
        """
        Returns weight uom for the sale
        """
        ModelData = Pool().get('ir.model.data')

        return ModelData.get_id('product', 'uom_pound')

    def get_weight(self, name):
        """
        Returns sum of weight associated with each line
        """
        return sum(
            map(lambda line: line.get_weight(self.weight_uom, silent=True),
                self.lines))

    @fields.depends('party', 'shipment_address', 'warehouse')
    def on_change_with_is_international_shipping(self, name=None):
        """
        Return True if international shipping
        """
        from_address = self._get_ship_from_address(silent=True)

        if self.shipment_address and from_address and \
                from_address.country and self.shipment_address.country and \
                from_address.country != self.shipment_address.country:
            return True
        return False

    def _get_ship_from_address(self, silent=False):
        """
        Usually the warehouse from which you ship
        """
        if not self.warehouse.address and not silent:
            return self.raise_user_error('warehouse_address_missing')
        return self.warehouse and self.warehouse.address

    def add_shipping_line(self,
                          shipment_cost,
                          description,
                          carrier=None,
                          carrier_service=None):
        """
        This method takes shipping_cost and description as arguments and writes
        a shipping line. It deletes any previous shipping lines which have
        a shipment_cost.
        :param shipment_cost: The shipment cost calculated according to carrier
        :param description: Shipping line description
        """
        Sale = Pool().get('sale.sale')

        carrier = carrier or self.carrier
        carrier_service = carrier_service and self.carrier_service

        Sale.write(
            [self],
            {
                'lines': [
                    (
                        'create',
                        [{
                            'type': 'line',
                            'product': carrier.carrier_product.id,
                            'description': description,
                            'quantity': 1,  # XXX
                            'unit': carrier.carrier_product.sale_uom.id,
                            'unit_price': shipment_cost,
                            'shipment_cost': shipment_cost,
                            'amount': shipment_cost,
                            'taxes': [],
                            'sequence': 9999,  # XXX
                        }]),
                    ('delete', [
                        line for line in self.lines
                        if line.shipment_cost is not None
                    ]),
                ],
                # reset the amount caches or function
                # fields will continue to return cached values
                'untaxed_amount_cache':
                None,
                'tax_amount_cache':
                None,
                'total_amount_cache':
                None,
            })
        # reset the order total cache
        if self.state not in ('draft', 'quote'):
            Sale.store_cache([self])

    def _get_carrier_context(self):
        "Pass sale in the context"
        context = super(Sale, self)._get_carrier_context()
        context = context.copy()
        context['sale'] = self.id
        return context

    def apply_shipping_rate(self, rate):
        """
        This method applies shipping rate. Rate is a dictionary with following
        minimum keys:

            {
                'display_name': Name to display,
                'carrier_service': carrier.service active record,
                'cost': cost,
                'cost_currency': currency.currency active record,
                'carrier': carrier active record,
            }

        It also creates a shipment line by deleting all existing ones.

        The rate could optionally have integer ids of service, carrier and
        currency.
        """
        Currency = Pool().get('currency.currency')
        Carrier = Pool().get('carrier')
        CarrierService = Pool().get('carrier.service')

        self.carrier = Carrier(int(rate['carrier']))
        self.carrier_service = CarrierService(int(rate['carrier_service'])) \
            if rate['carrier_service'] else None
        self.save()

        cost_currency = Currency(int(rate['cost_currency']))
        shipment_cost = cost_currency.round(rate['cost'])
        if self.currency != cost_currency:
            shipment_cost = Currency.compute(cost_currency, shipment_cost,
                                             self.currency)

        self.add_shipping_line(
            shipment_cost,
            rate['display_name'],
            self.carrier,
            self.carrier_service,
        )

    def get_shipping_rates(self, carriers=None, silent=False):
        """
        Gives a list of rates from carriers provided. If no carriers provided,
        return rates from all the carriers.

        List contains dictionary with following minimum keys:
            [
                {
                    'display_name': Name to display,
                    'carrier_service': carrier.service active record,
                    'cost': cost,
                    'cost_currency': currency.currency active repord,
                    'carrier': carrier active record,
                }..
            ]
        """
        Carrier = Pool().get('carrier')

        if carriers is None:
            carriers = Carrier.search([])

        rates = []
        for carrier in carriers:
            rates.extend(self.get_shipping_rate(carrier, silent=silent))
        return rates

    def get_shipping_rate(self, carrier, carrier_service=None, silent=False):
        """
        Gives a list of rates from provided carrier and carrier service.

        List contains dictionary with following minimum keys:
            [
                {
                    'display_name': Name to display,
                    'carrier_service': carrier.service active record,
                    'cost': cost,
                    'cost_currency': currency.currency active repord,
                    'carrier': carrier active record,
                }..
            ]
        """
        Company = Pool().get('company.company')

        if carrier.carrier_cost_method == 'product':
            currency = Company(Transaction().context['company']).currency
            rate_dict = {
                'carrier_service': carrier_service,
                'cost': carrier.carrier_product.list_price,
                'cost_currency': currency,
                'carrier': carrier,
            }
            display_name = carrier.rec_name
            rate_dict['display_name'] = display_name
            return [rate_dict]

        return []

    @classmethod
    def get_allowed_carriers_domain(cls):
        """This method returns domain to seach allowed carriers

        Downstream modules can inherit and update customize this domain.
        """
        return []

    def create_shipment(self, shipment_type):
        with Transaction().set_context(ignore_carrier_computation=True):
            shipments = super(Sale, self).create_shipment(shipment_type)

        if shipment_type == 'out' and shipments:
            for shipment in shipments:
                if shipment.carrier:
                    continue
                shipment.carrier = self.carrier
                shipment.carrier_service = self.carrier_service
                shipment.save()

        return shipments
 def view_attributes(cls):
     return super(Template, cls).view_attributes() + [
         ('//page[@id="counting"]', 'states', {
             'invisible': Not(Equal(Eval('type'), 'goods')),
         })
     ]
Ejemplo n.º 14
0
class Iss(ModelSQL, ModelView):
    'Injury Surveillance System Registration'
    __name__ = 'gnuhealth.iss'

    name = fields.Many2One('gnuhealth.patient.evaluation',
                           'Evaluation',
                           required=True,
                           help='Related Patient Evaluation')

    injury_date = fields.Date('Injury Date',
                              help="Usually the same as the Evaluation")

    registration_date = fields.Date('Registration Date')

    code = fields.Char('Code', help='Injury Code', required=True)

    operational_sector = fields.Many2One(
        'gnuhealth.operational_sector',
        'O. Sector',
        help="Operational Sector in which happened the injury")

    latitude = fields.Numeric('Latidude', digits=(3, 14))
    longitude = fields.Numeric('Longitude', digits=(4, 14))

    urladdr = fields.Char(
        'OSM Map',
        help="Maps the Accident / Injury location on Open Street Map")

    healthcenter = fields.Many2One('gnuhealth.institution', 'Institution')

    patient = fields.Function(fields.Char('Patient'),
                              'get_patient',
                              searcher='search_patient')

    patient_sex = fields.Function(fields.Char('Gender'), 'get_patient_sex')

    patient_age = fields.Function(fields.Char('Age'), 'get_patient_age')

    complaint = fields.Function(fields.Char('Chief Complaint'),
                                'get_patient_complaint')

    injury_type = fields.Selection([
        (None, ''),
        ('accidental', 'Accidental / Unintentional'),
        ('violence', 'Violence'),
        ('attempt_suicide', 'Suicide Attempt'),
        ('motor_vehicle', 'Motor Vehicle'),
    ],
                                   'Injury Type',
                                   required=True,
                                   sort=False)

    mva_mode = fields.Selection(
        [
            (None, ''),
            ('pedestrian', 'Pedestrian'),
            ('bicycle', 'Bicycle'),
            ('motorbike', 'Motorbike'),
            ('car', 'Car'),
            ('van', 'Van / Pickup / Jeep'),
            ('truck', 'Truck / Heavy vehicle'),
            ('bus', 'Bus'),
            ('train', 'Train'),
            ('taxi', 'Taxi'),
            ('boat', 'Boat / Ship'),
            ('aircraft', 'Aircraft'),
            ('other', 'Other'),
            ('unknown', 'Unknown'),
        ],
        'Mode',
        help="Motor Vehicle Accident Mode",
        sort=False,
        states={'required': Equal(Eval('injury_type'), 'motor_vehicle')})

    mva_position = fields.Selection(
        [
            (None, ''),
            ('driver', 'Driver'),
            ('passenger', 'Passenger'),
            ('outside', 'Outside / on the back'),
            ('bystander', 'Bystander'),
            ('unspecified_vehicle', 'Unspecified vehicle'),
            ('unknown', 'Unknown'),
        ],
        'User Position',
        help="Motor Vehicle Accident user position",
        sort=False,
        states={'required': Equal(Eval('injury_type'), 'motor_vehicle')})

    mva_counterpart = fields.Selection(
        [
            (None, ''),
            ('pedestrian', 'Pedestrian'),
            ('bicycle', 'Bicycle'),
            ('motorbike', 'Motorbike'),
            ('car', 'Car'),
            ('van', 'Van / Pickup / Jeep'),
            ('truck', 'Truck / Heavy vehicle'),
            ('bus', 'Bus'),
            ('train', 'Train'),
            ('taxi', 'Taxi'),
            ('boat', 'Boat / Ship'),
            ('aircraft', 'Aircraft'),
            ('other', 'Other'),
            ('unknown', 'Unknown'),
        ],
        'Counterpart',
        help="Motor Vehicle Accident Counterpart",
        sort=False,
        states={'required': Equal(Eval('injury_type'), 'motor_vehicle')})

    safety_gear = fields.Selection(
        [
            (None, ''),
            ('yes', 'Yes'),
            ('no', 'No'),
            ('unknown', 'Unknown'),
        ],
        'Safety Gear',
        help="Use of Safety Gear - Helmet, safety belt...",
        sort=False,
        states={'required': Equal(Eval('injury_type'), 'motor_vehicle')})

    alcohol = fields.Selection(
        [
            (None, ''),
            ('yes', 'Yes'),
            ('no', 'No'),
            ('suspected', 'Suspected'),
            ('unknown', 'Unknown'),
        ],
        'Alcohol',
        required=True,
        help="Is there evidence of alcohol use by the injured person"
        " in the 6 hours before the accident ?",
        sort=False)

    drugs = fields.Selection(
        [
            (None, ''),
            ('yes', 'Yes'),
            ('no', 'No'),
            ('suspected', 'Suspected'),
            ('unknown', 'Unknown'),
        ],
        'Other Drugs',
        required=True,
        help="Is there evidence of drug use by the injured person"
        " in the 6 hours before the accident ?",
        sort=False)

    injury_details = fields.Text('Details')

    # Add victim-perpretator relationship for violence-related injuries
    victim_perpetrator = fields.Selection(
        [
            (None, ''),
            ('parent', 'Parent'),
            ('spouse', 'Wife / Husband'),
            ('girlboyfriend', 'Girl / Boyfriend'),
            ('relative', 'Other relative'),
            ('acquaintance', 'Acquaintance / Friend'),
            ('official', 'Official / Legal'),
            ('stranger', 'Stranger'),
            ('other', 'other'),
        ],
        'Relationship',
        help="Victim - Perpetrator relationship",
        sort=False,
        states={'required': Equal(Eval('injury_type'), 'violence')})

    violence_circumstances = fields.Selection(
        [
            (None, ''),
            ('fight', 'Fight'),
            ('robbery', 'Robbery'),
            ('drug', 'Drug Related'),
            ('sexual', 'Sexual Assault'),
            ('gang', 'Gang Activity'),
            ('other_crime', 'Committing a crime (other)'),
            ('other', 'Other'),
            ('unknown', 'Unknown'),
        ],
        'Context',
        help="Precipitating Factor",
        sort=False,
        states={'required': Equal(Eval('injury_type'), 'violence')})

    injury_method = fields.Selection(
        [
            (None, ''),
            ('blunt', 'Blunt object'),
            ('push', 'Push/bodily force'),
            ('sharp', 'Sharp objects'),
            ('gun', 'Gun shot'),
            ('sexual', 'Sexual Assault'),
            ('choking', 'Choking/strangulation'),
            ('other', 'Other'),
            ('unknown', 'Unknown'),
        ],
        'Method',
        help="Method of Injury",
        sort=False,
        states={'required': Equal(Eval('injury_type'), 'violence')})

    # Place of occurrance . Not used in motor vehicle accidents

    place_occurrance = fields.Selection(
        [
            (None, ''),
            ('home', 'Home'),
            ('street', 'Street'),
            ('institution', 'Institution'),
            ('school', 'School'),
            ('commerce', 'Commercial Area'),
            ('publicbuilding', 'Public Building'),
            ('recreational', 'Recreational Area'),
            ('transportation', 'Public transportation'),
            ('sports', 'Sports event'),
            ('unknown', 'Unknown'),
        ],
        'Place',
        help="Place of occurrance",
        sort=False,
        states={'required': Not(Equal(Eval('injury_type'), 'motor_vehicle'))})

    disposition = fields.Selection([
        (None, ''),
        ('treated_sent', 'Treated and Sent Home'),
        ('admitted', 'Admitted to Ward'),
        ('observation', 'Admitted to Observation'),
        ('died', 'Died'),
        ('daa', 'Discharge Against Advise'),
        ('transferred', 'Transferred'),
        ('doa', 'Dead on Arrival'),
    ],
                                   'Disposition',
                                   help="Place of occurrance",
                                   sort=False,
                                   required=True)

    def get_patient(self, name):
        return self.name.patient.rec_name

    def get_patient_sex(self, name):
        return self.name.patient.name.gender

    def get_patient_age(self, name):
        return self.name.patient.name.age

    def get_patient_complaint(self, name):
        return self.name.chief_complaint

    @fields.depends('latitude', 'longitude')
    def on_change_with_urladdr(self):
        # Generates the URL to be used in OpenStreetMap
        # The address will be mapped to the URL in the following way
        # If the latitud and longitude of the Accident / Injury
        # are given, then those parameters will be used.

        ret_url = ''
        if (self.latitude and self.longitude):
            ret_url = 'http://openstreetmap.org/?mlat=' + \
                str(self.latitude) + '&mlon=' + str(self.longitude)

        return ret_url

    @classmethod
    def search_patient(cls, name, clause):
        res = []
        value = clause[2]
        res.append(('name.patient', clause[1], value))
        return res

    @classmethod
    def __setup__(cls):
        super(Iss, cls).__setup__()
        t = cls.__table__()
        cls._sql_constraints = [
            ('code_uniq', Unique(t, t.code),
             'This ISS registration Code already exists'),
        ]

    @classmethod
    def view_attributes(cls):
        return [
            ('//group[@id="motor_vehicle_accident"]', 'states', {
                'invisible': Not(Equal(Eval('injury_type'), 'motor_vehicle')),
            }),
            ('//group[@id="violent_injury"]', 'states', {
                'invisible': Not(Equal(Eval('injury_type'), 'violence')),
            }),
            ('//group[@id="iss_place"]', 'states', {
                'invisible': Equal(Eval('injury_type'), 'motor_vehicle'),
            }),
        ]
Ejemplo n.º 15
0
from werkzeug.exceptions import NotFound
from flask.ext.babel import format_currency

from trytond.model import ModelView, ModelSQL, fields
from trytond.pyson import Eval, Not, Bool
from trytond.transaction import Transaction
from trytond.pool import Pool, PoolMeta


__all__ = [
    'Product', 'ProductsImageSet', 'ProductsRelated', 'ProductCategory',
    'WebSite', 'WebsiteCategory', 'ProductTemplate',
]
__metaclass__ = PoolMeta

DEFAULT_STATE = {'invisible': Not(Bool(Eval('displayed_on_eshop')))}
DEFAULT_STATE2 = {
    'invisible': Not(Bool(Eval('displayed_on_eshop'))),
    'required': Bool(Eval('displayed_on_eshop')),
}


class ProductTemplate:
    __name__ = "product.template"

    products_displayed_on_eshop = fields.Function(
        fields.One2Many('product.product', None, 'Products (Disp. on eShop)'),
        'get_products_displayed_on_eshop'
    )

    def get_products_displayed_on_eshop(self, name=None):
Ejemplo n.º 16
0
class QueueEntry(ModelSQL, ModelView):
    'Queue Entry'
    __name__ = 'gnuhealth.patient.queue_entry'

    active = fields.Boolean('Active')
    triage_entry = fields.Many2One('gnuhealth.triage.entry', 'Triage Entry',
                                   states={'readonly': Eval('id', 0) > 0,
                                           'required': ~Eval('appointment')},
                                   select=True)
    appointment = fields.Many2One(
        'gnuhealth.appointment', 'Appointment', select=True,
        states={'readonly': Eval('id', 0) > 0,
                'required': ~Eval('triage_entry')})
    encounter = fields.Many2One('gnuhealth.encounter', 'Encounter',
                                states={'invisible': True})
    busy = fields.Boolean('Busy', states={'readonly': True}, select=True)
    called_by_me = fields.Function(fields.Boolean('Last Called By Me', 
                                                  states={'readonly': True,
                                                          'invisible': 
                                                          Not(Eval('called_by_me', 
                                                                   False))
                                                         }, 
                                                  select=True)
                                   , 'get_called_by_me')
    called_by = fields.Function(fields.Char('Last Called By', 
                                            states={'readonly': True}, 
                                            select=True)
                                , 'get_called_by')
    line_notes = fields.Text('Line notes',
                             help="Quick note about this line/patient")
    encounter_components = fields.Function(
        fields.Char('Encounter Components'), 'get_encounter_component_set')
    encounter_component_count = fields.Function(
        fields.Integer('Component Count'), 'get_encounter_component_set')
    entry_state = fields.Function(fields.Selection(QUEUE_ENTRY_STATES,
                                                   'State'), 'get_qentry_state',
                                  searcher='search_qentry_state')
    triage_status = fields.Function(fields.Char('triage status'), 'get_triage_status',
                                    searcher='search_triage_status')
    name = fields.Function(fields.Char('Name'), 'get_patient_name',
                           searcher='search_patient_name')
    upi_mrn_id = fields.Function(fields.Char('UPI/MRN/ID#'), 'get_upi_mrn_id',
                                 searcher='search_upi_mrn_id')
    sex = fields.Function(fields.Selection(SEX_OPTIONS, 'Sex'),
                          'get_sex', searcher='search_sex')
    age = fields.Function(fields.Char('Age'), 'get_age')
    primary_complaint = fields.Function(fields.Char('Primary Complaint'),
                                        'get_primary_complaint',
                                        searcher='search_primary_complaint')
    notes = fields.Function(fields.Text('Notes/Info'), 'get_notes_info')
    queue_notes = fields.One2Many('gnuhealth.patient.queue_entry_note',
                                  'queue_entry', 'Queue Notes',
                                  states={'invisible': True})
    last_call = fields.DateTime('Last Called', select=True)
    priority = fields.Integer('Priority', states={'readonly': True})
    last_touch = fields.Function(fields.DateTime('Last Seen', format='%H:%M'),
                                 'get_last_touch')
    last_toucher = fields.Function(fields.Char('Modification User'),
                                   'get_last_touch')
    specialty = fields.Function(fields.Many2One('gnuhealth.specialty',
                                                'Specialty'),
                                'get_specialty', searcher='search_specialty')
    timehere = fields.Function(fields.Char('Time Here'), 'get_time_here')
    visit_reason = fields.Function(fields.Many2One('gnuhealth.pathology',
                                                   'Reason for Visit'),
                                   getter='get_visit_reason')

    @staticmethod
    def default_busy():
        return False

    @staticmethod
    def default_active():
        return True

    @staticmethod
    def default_priority():
        return TRIAGE_MAX_PRIO

    @classmethod
    def __setup__(cls):
        super(QueueEntry, cls).__setup__()
        cls.write_date.string = 'Last Modified'
        cls._order = [('priority', 'ASC'), ('last_call', 'ASC'),
                      ('create_date', 'ASC')]
        cls._buttons.update(
            btn_inspect={},
            btn_call={'readonly': Or(Eval('busy', False),
                                     Equal('99', Eval('entry_state', '0')))},
            btn_dismiss={'readonly': Not(Eval('busy', False)), 
                         'invisible': Not(Eval('called_by_me', False))},
            btn_setup_appointment={
                'invisible': Or(Bool(Eval('appointment')),
                                ~Equal('20', Eval('entry_state', '0')))
            })
        cls._sql_constraints += [
            ('triage_uniq', 'UNIQUE(triage_entry)',
             'Triage entry already in the queue'),
            ('appointment_uniq', 'UNIQUE(appointment)',
             'Appointment already in the queue')]

    @classmethod
    def _swapout(cls, vdict, is_write=True):
        if vdict.get('line_notes', False):
            note = ('create', [{'note': vdict.pop('line_notes')}])
            # if is_write:
            #     note = ('create', [note])
            vdict.setdefault('queue_notes', []).append(note)
        if vdict.get('busy', False):
            vdict['last_call'] = datetime.now()
        return vdict

    @classmethod
    def write(cls, instances, values, *args):
        # overload to handle the following situation:
        # if something is written in line-notes, create a QueueEntryNote
        # object with that as the note. To do that we will
        arglist = iter((instances, values) + args)
        log_entries = []
        for recs, vals in zip(arglist, arglist):
            vals = cls._swapout(vals)
            if 'busy' in vals:
                # call or dismiss
                new_busy = vals['busy']
                for rec in recs:
                    log_entries.append(
                        {'queue_entry': rec.id,
                         'entry_state': rec.entry_state,
                         'action': 'call' if new_busy else 'dismiss'})
        retval = super(QueueEntry, cls).write(instances, values, *args)
        QueueCallLog.create(log_entries)
        return retval

    @classmethod
    def create(cls, vlist):
        # overload to create a QueueEntryNote if a line_note is included
        newvlist = [cls._swapout(v, False) for v in vlist]
        return super(QueueEntry, cls).create(newvlist)

    def get_patient_name(self, name):
        if self.appointment:
            return self.appointment.patient.name.name
        elif self.triage_entry:
            return self.triage_entry.name
        else:
            return '[No Name]'

    def get_last_touch(self, name):
        if name == 'last_touch':
            return self.write_date and self.write_date or self.create_date
        elif name == 'last_toucher':
            return self.write_uid.name if self.write_uid else None
        return ''

    def get_triage_status(self, name):
        return self.triage_entry.status if self.triage_entry else ''

    @classmethod
    def search_triage_status(cls, name, clause):
        fld, operator, operand = clause
        return [('triage_entry', '!=', None)
                ('triage_entry.status', operator, operand)]

    @classmethod
    def get_time_here(cls, instances, name):
        def iget_start_end(i, now=None):
            triage = i.triage_entry
            appt = i.appointment
            # Work out start time
            if triage:
                start = i.triage_entry.create_date
            elif appt:
                state_changes = [x for x in appt.state_changes
                                 if x.target_state == 'arrived']
                if state_changes:
                    start = state_changes[0].create_date
                else:
                    start = appt.create_date
            else:
                start = i.create_date

            # Work out End Time
            if appt:
                if appt.state in APPT_DONE_STATES:
                    state_changes = [x for x in appt.state_changes
                                     if x.target_state in APPT_DONE_STATES]
                    if state_changes:
                        end = state_changes[-1].create_date
                    else:
                        end = appt.write_date
                else:
                    end = now
            elif triage:
                if i.entry_state == '99':
                    end = triage.end_time
                else:
                    end = now
            elif i.entry_state == '99':
                end = i.write_date
            else:
                end = now
            return (start, end)
        Now = datetime.now()

        def dmapper(i):
            return (int(i), get_elapsed_time(*iget_start_end(i, Now)))

        return dict(map(dmapper, instances))

    @classmethod
    def search_patient_name(cls, name, clause):
        fld, operator, operand = clause
        if name == 'name':
            dom = ['OR']
            for fld in ('triage_entry.name',
                        'triage_entry.patient.name.name',
                        'appointment.patient.name.name'):
                dom.append((fld, operator, operand))
            return dom

    def get_upi_mrn_id(self, name):
        if self.appointment:
            return '%s / %s' % (self.appointment.upi,
                               self.appointment.medical_record_num)
        elif self.triage_entry:
            return self.triage_entry.id_display
        return ''

    @classmethod
    def search_upi_mrn_id(cls, name, clause):
        fld, operator, operand = clause
        out = ['OR'] + [(f, operator, operand) for f in
                        ('triage_entry.id_number',
                         'appointment.patient.puid',
                         'triage_entry.patient.puid',
                         'triage_entry.patient.medical_record_num')]
        return out

    # get_encounter_component_set = returns a csv string with the most
    # recent component listed first
    def get_encounter_component_set(self, name):
        if self.encounter:
            if name == 'encounter_component_count':
                return len(self.encounter.components)
            elif name == 'encounter_components':
                complist = [x.component_type
                            for x in self.encounter.components]
                return ', '.join(reversed(complist))

        if name == 'encounter_component_count':
            return 0
        else:  # name == 'encounter_components':
            return ''

    # search_component_set : a domain that searches for queue items
    # with encounters that have component types entered

    # search_component_count: return a domain to search for queue entries
    # based on the number of components the associated encounter has

    # get_qentry_state: return one from QUEUE_ENTRY_STATES based
    # on the state of the associated appointment and encounter
    def get_qentry_state(self, name):
        if self.appointment:
            if self.appointment.state == 'arrived':
                return '30'
            elif self.appointment.state == 'processing':
                return '40'
            elif self.appointment.state in APPT_DONE_STATES:
                return '99'
        elif self.triage_entry:
            triage_entry = self.triage_entry
            if self.triage_entry.done:
                if (triage_entry.status in TRIAGE_REG_STATES[:2] and
                        not triage_entry.post_appointment):
                    return '20'
                else:
                    return '99'
            elif triage_entry.status in TRIAGE_REG_STATES:
                return '20'
            elif triage_entry.status == 'triage':
                return '12'
        return '10'

    # search_qentry_state: domain that searches queueEntries based
    # the specified state entered.
    @classmethod
    def search_qentry_state(cls, name, clause):
        field, operator, operand = clause
        if operator == '=':
            # the easy one and maybe the only one we'll need since
            # entry_state is a selection field
            if operand == '10':
                return ['OR', ('triage_entry.status', '=', 'pending'),
                        ('appointment.state', '=', 'confirmed')]
            elif operand == '12':
                return [('triage_entry.status', '=', 'triage')]
            elif operand == '20':
                return [('triage_entry.status', 'in', TRIAGE_REG_STATES)]
            elif operand == '30':
                return [('appointment.state', '=', 'arrived')]
            elif operand == '40':
                return [('appointment.state', '=', 'processing')]
            else:
                return ['OR', ('appointment.state', 'in', APPT_DONE_STATES),
                        ['AND', ('appointment', '=', None),
                         ['OR',
                          ['AND', ('triage_entry.done', '=', True), ('triage_entry.status', 'not in', TRIAGE_REG_STATES[:2])],
                          ['AND', ('triage_entry.done', '=', True), ('triage_entry.post_appointment', '!=', None)]]]]
        elif operator == '!=':
            if operand == '99':
                # i.e. those that are not done
                return ['OR', ('appointment.state', 'not in', APPT_DONE_STATES),
                        ['AND', ('appointment', '=', None),
                         ['OR', ('triage_entry.done', '=', False),
                          [('triage_entry.done', '=', True), ('triage_entry.status', 'in', TRIAGE_REG_STATES[:2]), ('triage_entry.post_appointment', '=', None)]]]]
            if operand == '10':
                return [
                    'OR',
                    ('triage_entry.status', 'in', ['triage'] + TRIAGE_REG_STATES),
                    ('appointment.state', 'in', ['arrived', 'processing'])]

    def get_sex(self, name):
        if self.appointment and self.appointment.patient:
            return self.appointment.patient.name.sex
        elif self.triage_entry:
            return self.triage_entry.sex
        else:
            return '--'

    @classmethod
    def search_sex(cls, name, clause):
        fld, operator, operand = clause
        return ['OR', ('triage_entry.sex', operator, operand),
                ('appointment.patient.name.sex', operator, operand)]

    def get_age(self, name):
        if self.appointment:
            return self.appointment.age
        else:
            try:
                return self.triage_entry.age
            except AttributeError:
                return '--'

    # @classmethod
    # def search_age(cls, name, clause):
    #     pass

    def get_notes_info(self, name):
        details = []
        qnotes = []
        if self.queue_notes:
            qnotes = map(lambda x: u' - '.join([x.note, x.byline]),
                         self.queue_notes)
            details.extend([qnotes.pop(0), '-' * 20])
        if self.encounter:
            details.extend(['Encounter started: %s' % (
                       localtime(self.encounter.start_time).strftime('%F %T'),),
                       '    %s' % self.encounter.short_summary])
        elif self.appointment:
            a = self.appointment
            details.extend(
                [u' '.join(x) for x in [
                    (u'Appointment: ',
                     localtime(a.appointment_date).strftime('%F %T')),
                    (u'    Specialty: ', a.speciality.name),
                    (u'    Status: ', a.state)]])
            if a.visit_reason:
                details.insert(-2, ' '.join((u'    Reason for visit:',
                                             a.visit_reason.rec_name)))
            # details.append('')
        # else:
        if self.triage_entry:
            details.append('Triage, first contact: %s,\n    status: %s' % (
                           localtime(
                               self.triage_entry.create_date).strftime('%F %T'),
                           self.triage_entry.status_display))
            details.extend(['    %s' % x for x in
                            filter(None, [self.triage_entry.complaint,
                                          self.triage_entry.notes])])
        if qnotes:
            details.extend(['-' * 20] + qnotes)
        return u'\n'.join(details)

    @classmethod
    def get_primary_complaint(cls, instances, name):
        outd = {}
        for i in instances:
            ix = i.id
            outd[ix] = ''
            if i.encounter:
                outd[ix] = i.encounter.primary_complaint
            if i.triage_entry and not outd[ix]:
                outd[ix] = i.triage_entry.complaint
        return outd

    @classmethod
    def search_primary_complaint(cls, name, clause):
        subclause = tuple(clause[1:])
        tclause = ('triage_entry.complaint', ) + subclause
        eclause = ('encounter.primary_complaint', ) + subclause
        return ['OR', tclause, eclause]

    def get_specialty(self, name):
        if self.appointment:
            return self.appointment.speciality.id
        else:
            # return '[Triage]'
            return None

    @classmethod
    def search_specialty(cls, name, clause):
        subclause = tuple(clause[1:])
        newclause = ('appointment.speciality', ) + subclause
        return ['AND', ('appointment', '!=', None), newclause]

    # Button Functions for :
    # Inspect: Does the same as call except doesn't create new records nor
    #          does it update the busy flag on the queue entry
    # Call: sets busy=True for the selected queue item and opens the
    #        Triage, Appointment or Encounter object associated
    #        If the required related Appointment or Encounter no exist
    #        create it.
    # Dismiss: Sets busy=False for the selected queue item.

    @classmethod
    @ModelView.button_action('health_triage_queue.act_queue_inspect_starter')
    def btn_inspect(cls, queue_entries):
        pass

    @classmethod
    @ModelView.button_action('health_triage_queue.act_queue_call_starter')
    def btn_call(cls, queue_entries):
        user = Transaction().user
        queue_model = Pool().get('gnuhealth.patient.queue_entry')
        patients_called_by_user = queue_model.search(
                ['AND', ('busy', '=', True), ('write_uid', '=', user)])
        if len(patients_called_by_user) >= MAX_QUEUE_CALL:
            patient_names = []
            for num, patient in enumerate(patients_called_by_user):
                (last_name, first_name) = patient.name.split(',')
                patient_names.append(
                        '    {}. {} {}'.format(num + 1, first_name, last_name))
            msg = ['You have exceeded the maximum number of',
                   'patients that you can call at once. Please',
                   'finish up and dismiss some of them before',
                   'calling anyone else', '',
                   'The ones you have called are:']
            msg.extend(patient_names)
            cls.raise_user_error('\n'.join(msg))

        # cls.write(queue_entries, {'busy': True})
        # we want to set this to True, but do we want to do it here
        # or should we do it after the thing launched has been saved or
        # somehow touched?
        pass

    @classmethod
    @ModelView.button_action('health_triage_queue.act_queue_dismiss_starter')
    def btn_dismiss(cls, queue_entries):
        pass

    @classmethod
    @ModelView.button_action('health_triage_queue.wiz_queue_appointment_setup')
    def btn_setup_appointment(cls, queue_entries):
        pass

    @classmethod
    def appointment_arrive_trigger(cls, appointments, trigger):
        # create a queue entry item for each appointment with state=arrived
        # 1st, find the appointments already queued
        already = cls.search_read(
            [('appointment', 'in', map(int, appointments))],
            fields_names=['appointment', 'id'])
        alreadict = dict([(x['appointment'], x['id']) for x in already])
        vals = [{'appointment': a.id, 'priority': APM.get(a.urgency, 0),
                 'busy': False}
                for a in appointments if a.id not in alreadict]
        cls.create(vals)

    def get_visit_reason(self, name):
        '''get reason for visit'''
        if self.appointment and self.appointment.visit_reason:
            return self.appointment.visit_reason.id
        return None

    def get_called_by(self, name):
        '''gets the user who call the selected patient'''
        return self.write_uid.name if self.write_uid else ''


    def get_called_by_me(self, name):
        '''Determines if the carrent selected patient has been 
           called by the current user. Returns true if yes and 
           false otherwise'''
        user = Transaction().user
        return self.write_uid.id == user if self.write_uid else False
Ejemplo n.º 17
0
class Appointment(metaclass=PoolMeta):
    __name__ = 'gnuhealth.appointment'

    event = fields.Many2One('calendar.event',
                            'CalDAV Event',
                            readonly=True,
                            help="Calendar Event",
                            states={'invisible': Not(Bool(Eval('event')))})
    appointment_date_end = fields.DateTime('End Date and Time')

    @classmethod
    def create(cls, vlist):
        pool = Pool()
        Event = pool.get('calendar.event')
        Patient = pool.get('gnuhealth.patient')
        Healthprof = pool.get('gnuhealth.healthprofessional')

        vlist = [x.copy() for x in vlist]

        for values in vlist:
            # Check if the event exists (eg, created from a caldav client)
            # If so, then do not create it again.
            if 'event' in values:
                break

            if values['state'] == 'confirmed':
                if values['healthprof']:
                    healthprof = Healthprof(values['healthprof'])
                    if (healthprof.name.internal_user
                            and healthprof.name.internal_user.calendar):
                        patient = Patient(values['patient'])
                        appointment_date_end = None
                        if values.get('appointment_date_end'):
                            appointment_date_end = \
                                values['appointment_date_end']
                        events = Event.create([{
                            'dtstart':
                            values['appointment_date'],
                            'dtend':
                            appointment_date_end,
                            'calendar':
                            healthprof.name.internal_user.calendar.id,
                            'summary':
                            patient.name.rec_name,
                            'description':
                            values['comments'],
                        }])
                        values['event'] = events[0].id
        return super(Appointment, cls).create(vlist)

    @classmethod
    def write(cls, appointments, values):
        pool = Pool()
        Event = pool.get('calendar.event')
        Patient = pool.get('gnuhealth.patient')
        Healthprof = pool.get('gnuhealth.healthprofessional')

        for appointment in appointments:
            #Update caldav event
            if appointment.event and ('healthprof' not in values):
                if 'appointment_date' in values:
                    Event.write([appointment.event], {
                        'dtstart': values['appointment_date'],
                    })
                if 'appointment_date_end' in values:
                    Event.write([appointment.event], {
                        'dtend': values['appointment_date_end'],
                    })
                if 'patient' in values:
                    patient = Patient(values['patient'])
                    Event.write([appointment.event], {
                        'summary': patient.name.rec_name,
                    })
                if 'comments' in values:
                    Event.write([appointment.event], {
                        'description': values['comments'],
                    })

            else:
                #Move the event to the new health professional
                if appointment.event and ('healthprof' in values):
                    current_event = [appointment.event]
                    if appointment.healthprof.name.internal_user:
                        healthprof = Healthprof(values['healthprof'])
                        if healthprof.name.internal_user.calendar:
                            #Health professional has calendar
                            patient = appointment.patient.name.rec_name
                            comments = ''
                            if 'comments' in values:
                                comments = values['comments']
                            else:
                                comments = appointment.comments
                            if 'appointment_date' in values:
                                appointment_date = values['appointment_date']
                            else:
                                appointment_date = appointment.appointment_date
                            if 'appointment_date_end' in values:
                                appointment_date_end = values[
                                    'appointment_date_end']
                            else:
                                appointment_date_end = appointment.appointment_date_end
                            events = Event.create([{
                                'dtstart':
                                appointment_date,
                                'dtend':
                                appointment_date_end,
                                'calendar':
                                healthprof.name.internal_user.calendar.id,
                                'summary':
                                patient,
                                'description':
                                comments,
                            }])
                            values['event'] = events[0].id

                    # Delete the event from the current health professional
                    # after it has been transfer to the new healthpfof
                    Event.delete(current_event)
        return super(Appointment, cls).write(appointments, values)

    @classmethod
    def delete(cls, appointments):
        Event = Pool().get('calendar.event')

        for appointment in appointments:
            if appointment.event:
                Event.delete([appointment.event])
        return super(Appointment, cls).delete(appointments)
Ejemplo n.º 18
0
Archivo: user.py Proyecto: uzayr/nereid
    """
    password = PasswordField(_('New Password'), [
        validators.DataRequired(),
        validators.EqualTo('confirm', message=_('Passwords must match'))])
    confirm = PasswordField(_('Repeat Password'))


class ChangePasswordForm(NewPasswordForm):
    """
    Form to change the password
    """
    old_password = PasswordField(_('Old Password'), [validators.DataRequired()])


STATES = {
    'readonly': Not(Bool(Eval('active'))),
}


class ProfileForm(Form):
    """User Profile Form"""
    display_name = TextField(
        'Display Name', [validators.DataRequired(), ],
        description="Your display name"
    )
    timezone = SelectField(
        'Timezone',
        choices=[(tz, tz) for tz in pytz.common_timezones],
        coerce=unicode, description="Your timezone"
    )
    email = TextField(
Ejemplo n.º 19
0
class OrderPoint(ModelSQL, ModelView):
    """
    Order Point
    Provide a way to define a supply policy for each
    product on each locations. Order points on warehouse are
    considered by the supply scheduler to generate purchase requests.
    """
    __name__ = 'stock.order_point'
    product = fields.Many2One('product.product',
                              'Product',
                              required=True,
                              select=True,
                              domain=[
                                  ('type', '=', 'goods'),
                                  ('consumable', '=', False),
                                  ('purchasable', 'in',
                                   If(Equal(Eval('type'), 'purchase'), [True],
                                      [True, False])),
                              ],
                              depends=['type'])
    warehouse_location = fields.Many2One('stock.location',
                                         'Warehouse Location',
                                         select=True,
                                         domain=[('type', '=', 'warehouse')],
                                         states={
                                             'invisible':
                                             Not(
                                                 Equal(Eval('type'),
                                                       'purchase')),
                                             'required':
                                             Equal(Eval('type'), 'purchase'),
                                         },
                                         depends=['type'])
    storage_location = fields.Many2One('stock.location',
                                       'Storage Location',
                                       select=True,
                                       domain=[('type', '=', 'storage')],
                                       states={
                                           'invisible':
                                           Not(Equal(Eval('type'),
                                                     'internal')),
                                           'required':
                                           Equal(Eval('type'), 'internal'),
                                       },
                                       depends=['type'])
    location = fields.Function(fields.Many2One('stock.location', 'Location'),
                               'get_location',
                               searcher='search_location')
    provisioning_location = fields.Many2One(
        'stock.location',
        'Provisioning Location',
        domain=[('type', 'in', ['storage', 'view'])],
        states={
            'invisible':
            Not(Equal(Eval('type'), 'internal')),
            'required': ((Eval('type') == 'internal')
                         & (Eval('min_quantity', None) != None)),
        },
        depends=['type', 'min_quantity'])
    overflowing_location = fields.Many2One(
        'stock.location',
        'Overflowing Location',
        domain=[('type', 'in', ['storage', 'view'])],
        states={
            'invisible':
            Eval('type') != 'internal',
            'required': ((Eval('type') == 'internal')
                         & (Eval('max_quantity', None) != None)),
        },
        depends=['type', 'max_quantity'])
    type = fields.Selection([('internal', 'Internal'),
                             ('purchase', 'Purchase')],
                            'Type',
                            select=True,
                            required=True)
    min_quantity = fields.Float(
        'Minimal Quantity',
        digits=(16, Eval('unit_digits', 2)),
        states={
            # required for purchase and production types
            'required': Eval('type') != 'internal',
        },
        domain=[
            'OR',
            ('min_quantity', '=', None),
            ('min_quantity', '<=', Eval('target_quantity', 0)),
        ],
        depends=['unit_digits', 'target_quantity', 'type'])
    target_quantity = fields.Float(
        'Target Quantity',
        required=True,
        digits=(16, Eval('unit_digits', 2)),
        domain=[
            [
                'OR',
                ('min_quantity', '=', None),
                ('target_quantity', '>=', Eval('min_quantity', 0)),
            ],
            [
                'OR',
                ('max_quantity', '=', None),
                ('target_quantity', '<=', Eval('max_quantity', 0)),
            ],
        ],
        depends=['unit_digits', 'min_quantity', 'max_quantity'])
    max_quantity = fields.Float(
        'Maximal Quantity',
        digits=(16, Eval('unit_digits', 2)),
        states={
            'invisible': Eval('type') != 'internal',
        },
        domain=[
            'OR',
            ('max_quantity', '=', None),
            ('max_quantity', '>=', Eval('target_quantity', 0)),
        ],
        depends=['unit_digits', 'type', 'target_quantity'])
    company = fields.Many2One('company.company',
                              'Company',
                              required=True,
                              domain=[
                                  ('id',
                                   If(In('company', Eval('context', {})), '=',
                                      '!='), Eval('context',
                                                  {}).get('company', -1)),
                              ])
    unit = fields.Function(fields.Many2One('product.uom', 'Unit'), 'get_unit')
    unit_digits = fields.Function(fields.Integer('Unit Digits'),
                                  'get_unit_digits')

    @classmethod
    def __register__(cls, module_name):
        cursor = Transaction().connection.cursor()
        sql_table = cls.__table__()
        table = cls.__table_handler__(module_name)

        # Migration from 4.2
        table.drop_constraint('check_max_qty_greater_min_qty')
        table.not_null_action('min_quantity', 'remove')
        table.not_null_action('max_quantity', 'remove')
        target_qty_exist = table.column_exist('target_quantity')

        super(OrderPoint, cls).__register__(module_name)

        # Migration from 4.2
        if not target_qty_exist:
            cursor.execute(*sql_table.update(
                [sql_table.target_quantity, sql_table.max_quantity],
                [sql_table.max_quantity, Null]))

    @staticmethod
    def default_type():
        return "purchase"

    @fields.depends('product', '_parent_product.default_uom')
    def on_change_product(self):
        self.unit = None
        self.unit_digits = 2
        if self.product:
            self.unit = self.product.default_uom
            self.unit_digits = self.product.default_uom.digits

    def get_unit(self, name):
        return self.product.default_uom.id

    def get_unit_digits(self, name):
        return self.product.default_uom.digits

    @classmethod
    def validate(cls, orderpoints):
        super(OrderPoint, cls).validate(orderpoints)
        cls.check_concurrent_internal(orderpoints)
        cls.check_uniqueness(orderpoints)

    @classmethod
    def check_concurrent_internal(cls, orders):
        """
        Ensure that there is no 'concurrent' internal order
        points. I.E. no two order point with opposite location for the
        same product and same company.
        """
        internals = cls.search([
            ('id', 'in', [o.id for o in orders]),
            ('type', '=', 'internal'),
        ])
        if not internals:
            return

        for location_name in ['provisioning_location', 'overflowing_location']:
            query = []
            for op in internals:
                if getattr(op, location_name, None) is None:
                    continue
                arg = [
                    'AND', ('product', '=', op.product.id),
                    (location_name, '=', op.storage_location.id),
                    ('storage_location', '=', getattr(op, location_name).id),
                    ('company', '=', op.company.id), ('type', '=', 'internal')
                ]
                query.append(arg)
            if query and cls.search(['OR'] + query):
                raise OrderPointValidationError(
                    gettext('stock_supply'
                            '.msg_order_point_concurrent_%s_internal' %
                            location_name))

    @staticmethod
    def _type2field(type=None):
        t2f = {
            'purchase': 'warehouse_location',
            'internal': 'storage_location',
        }
        if type is None:
            return t2f
        else:
            return t2f[type]

    @classmethod
    def check_uniqueness(cls, orders):
        """
        Ensure uniqueness of order points. I.E that there is no several
        order point for the same location, the same product and the
        same company.
        """
        query = ['OR']
        for op in orders:
            field = cls._type2field(op.type)
            arg = [
                'AND',
                ('product', '=', op.product.id),
                (field, '=', getattr(op, field).id),
                ('id', '!=', op.id),
                ('company', '=', op.company.id),
            ]
            query.append(arg)
        if cls.search(query):
            raise OrderPointValidationError(
                gettext('stock_supply.msg_order_point_unique'))

    def get_rec_name(self, name):
        return "%s @ %s" % (self.product.name, self.location.name)

    @classmethod
    def search_rec_name(cls, name, clause):
        return [
            'OR',
            ('location.rec_name', ) + tuple(clause[1:]),
            ('product.rec_name', ) + tuple(clause[1:]),
        ]

    def get_location(self, name):
        if self.type == 'purchase':
            return self.warehouse_location.id
        elif self.type == 'internal':
            return self.storage_location.id

    @classmethod
    def search_location(cls, name, domain=None):
        clauses = ['OR']
        for type, field in cls._type2field().items():
            clauses.append([
                ('type', '=', type),
                (field, ) + tuple(domain[1:]),
            ])
        return clauses

    @staticmethod
    def default_company():
        return Transaction().context.get('company')
Ejemplo n.º 20
0
class OrderPoint(ModelSQL, ModelView):
    """
    Order Point
    Provide a way to define a supply policy for each
    product on each locations. Order points on warehouse are
    considered by the supply scheduler to generate purchase requests.
    """
    __name__ = 'stock.order_point'
    product = fields.Many2One('product.product', 'Product', required=True,
        select=True,
        domain=[
            ('type', '=', 'goods'),
            ('consumable', '=', False),
            ('purchasable', 'in', If(Equal(Eval('type'), 'purchase'),
                    [True], [True, False])),
            ],
        depends=['type'])
    warehouse_location = fields.Many2One('stock.location',
        'Warehouse Location', select=True,
        domain=[('type', '=', 'warehouse')],
        states={
            'invisible': Not(Equal(Eval('type'), 'purchase')),
            'required': Equal(Eval('type'), 'purchase'),
            },
        depends=['type'])
    storage_location = fields.Many2One('stock.location', 'Storage Location',
        select=True,
        domain=[('type', '=', 'storage')],
        states={
            'invisible': Not(Equal(Eval('type'), 'internal')),
            'required': Equal(Eval('type'), 'internal'),
        },
        depends=['type'])
    location = fields.Function(fields.Many2One('stock.location', 'Location'),
            'get_location', searcher='search_location')
    provisioning_location = fields.Many2One(
        'stock.location', 'Provisioning Location',
        domain=[('type', 'in', ['storage', 'view'])],
        states={
            'invisible': Not(Equal(Eval('type'), 'internal')),
            'required': Equal(Eval('type'), 'internal'),
        },
        depends=['type'])
    type = fields.Selection(
        [('internal', 'Internal'),
         ('purchase', 'Purchase')],
        'Type', select=True, required=True)
    min_quantity = fields.Float('Minimal Quantity', required=True,
            digits=(16, Eval('unit_digits', 2)), depends=['unit_digits'])
    max_quantity = fields.Float('Maximal Quantity', required=True,
            digits=(16, Eval('unit_digits', 2)), depends=['unit_digits'])
    company = fields.Many2One('company.company', 'Company', required=True,
            domain=[
                ('id', If(In('company', Eval('context', {})), '=', '!='),
                    Eval('context', {}).get('company', -1)),
            ])
    unit = fields.Function(fields.Many2One('product.uom', 'Unit'), 'get_unit')
    unit_digits = fields.Function(fields.Integer('Unit Digits'),
            'get_unit_digits')

    @classmethod
    def __setup__(cls):
        super(OrderPoint, cls).__setup__()
        t = cls.__table__()
        cls._sql_constraints += [
            ('check_max_qty_greater_min_qty',
                Check(t, t.max_quantity >= t.min_quantity),
                'Maximal quantity must be bigger than Minimal quantity'),
            ]
        cls._error_messages.update({
                'unique_op': ('Only one order point is allowed '
                    'for each product-location pair.'),
                'concurrent_internal_op': ('You can not define '
                    'two order points on the same product '
                    'with opposite locations.'),
                })

    @classmethod
    def __register__(cls, module_name):
        TableHandler = backend.get('TableHandler')
        # Migration from 2.2
        table = TableHandler(cls, module_name)
        table.drop_constraint('check_min_max_quantity')

        super(OrderPoint, cls).__register__(module_name)

    @staticmethod
    def default_type():
        return "purchase"

    @fields.depends('product')
    def on_change_product(self):
        self.unit = None
        self.unit_digits = 2
        if self.product:
            self.unit = self.product.default_uom
            self.unit_digits = self.product.default_uom.digits

    def get_unit(self, name):
        return self.product.default_uom.id

    def get_unit_digits(self, name):
        return self.product.default_uom.digits

    @classmethod
    def validate(cls, orderpoints):
        super(OrderPoint, cls).validate(orderpoints)
        cls.check_concurrent_internal(orderpoints)
        cls.check_uniqueness(orderpoints)

    @classmethod
    def check_concurrent_internal(cls, orders):
        """
        Ensure that there is no 'concurrent' internal order
        points. I.E. no two order point with opposite location for the
        same product and same company.
        """
        internals = cls.search([
                ('id', 'in', [o.id for o in orders]),
                ('type', '=', 'internal'),
                ])
        if not internals:
            return

        query = ['OR']
        for op in internals:
            arg = ['AND',
                   ('product', '=', op.product.id),
                   ('provisioning_location', '=', op.storage_location.id),
                   ('storage_location', '=', op.provisioning_location.id),
                   ('company', '=', op.company.id),
                   ('type', '=', 'internal')]
            query.append(arg)
        if cls.search(query):
            cls.raise_user_error('concurrent_internal_op')

    @staticmethod
    def _type2field(type=None):
        t2f = {
            'purchase': 'warehouse_location',
            'internal': 'storage_location',
            }
        if type is None:
            return t2f
        else:
            return t2f[type]

    @classmethod
    def check_uniqueness(cls, orders):
        """
        Ensure uniqueness of order points. I.E that there is no several
        order point for the same location, the same product and the
        same company.
        """
        query = ['OR']
        for op in orders:
            field = cls._type2field(op.type)
            arg = ['AND',
                ('product', '=', op.product.id),
                (field, '=', getattr(op, field).id),
                ('id', '!=', op.id),
                ('company', '=', op.company.id),
                ]
            query.append(arg)
        if cls.search(query):
            cls.raise_user_error('unique_op')

    def get_rec_name(self, name):
        return "%s@%s" % (self.product.name, self.location.name)

    @classmethod
    def search_rec_name(cls, name, clause):
        res = []
        names = clause[2].split('@', 1)
        res.append(('product.template.name', clause[1], names[0]))
        if len(names) != 1 and names[1]:
            res.append(('location', clause[1], names[1]))
        return res

    def get_location(self, name):
        if self.type == 'purchase':
            return self.warehouse_location.id
        elif self.type == 'internal':
            return self.storage_location.id

    @classmethod
    def search_location(cls, name, domain=None):
        ids = []
        for type, field in cls._type2field().iteritems():
            args = [('type', '=', type)]
            for _, operator, operand in domain:
                args.append((field, operator, operand))
            ids.extend([o.id for o in cls.search(args)])
        return [('id', 'in', ids)]

    @staticmethod
    def default_company():
        return Transaction().context.get('company')
Ejemplo n.º 21
0
from trytond.model import ModelView, Workflow, ModelSQL, fields, Check, Unique
from trytond.wizard import Wizard, StateView, StateTransition, Button
from trytond.pyson import Not, Equal, Eval, Or, Bool, If
from trytond import backend
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.tools import reduce_ids, grouped_slice

__all__ = [
    'Forecast', 'ForecastLine', 'ForecastLineMove', 'ForecastCompleteAsk',
    'ForecastCompleteChoose', 'ForecastComplete'
]

STATES = {
    'readonly': Not(Equal(Eval('state'), 'draft')),
}
DEPENDS = ['state']


class Forecast(Workflow, ModelSQL, ModelView):
    "Stock Forecast"
    __name__ = "stock.forecast"
    _rec_name = 'warehouse'
    warehouse = fields.Many2One('stock.location',
                                'Location',
                                required=True,
                                domain=[('type', '=', 'warehouse')],
                                states={
                                    'readonly':
                                    Or(Not(Equal(Eval('state'), 'draft')),
Ejemplo n.º 22
0
class DocumentInvoiceGoods(ModelWorkflow, ModelSQL, ModelView):
    "Documents (Invoice) "
    _name='ekd.document.head.invoice_goods'
    _description=__doc__
    _inherits = {'ekd.document': 'document'}

    document = fields.Many2One('ekd.document', 'Document', required=True,
            ondelete='CASCADE')

    direct_document = fields.Selection([
                ('output_goods', 'For Customer'),
                ('move_goods', 'Internal Move'),
                ('input_goods', 'From Supplier'),
                ], 'Direct Document')

    type_product = fields.Selection([
                ('fixed_assets', 'Fixed Assets'),
                ('intangible', 'Intangible assets'),
                ('material', 'Material'),
                ('goods', 'Goods'),
                ], 'Type Product')

    template_invoice = fields.Function(fields.Many2One('ekd.document.template',
                'Name',
                domain=[('type_account', '=',
                    Eval('direct_document', 
                        Get(Eval('context', {}), 'direct_document')))]
                ), 'get_fields', setter='set_fields')
    number_doc = fields.Function(fields.Char('Number',
                states={'invisible': And(Not(Bool(Eval('number_doc'))),
                    Not(Equal(Eval('direct_document',''),'input_goods')))}),
                'get_fields', setter='set_fields', searcher='template_search')
    from_to_party = fields.Function(fields.Many2One('party.party',
                'Party',
                states={'invisible': Equal(Eval('direct_document'),'move_goods')}
                ), 'get_fields', setter='set_fields')
    from_party_fnc = fields.Function(fields.Many2One('party.party',
                'From Party',
                states={'invisible': Not(Equal(Eval('direct_document'),'move_goods'))}
                ), 'get_fields', setter='set_fields')
    to_party_fnc = fields.Function(fields.Many2One('party.party',
                'To Party',
                states={'invisible': Not(Equal(Eval('direct_document'),'move_goods'))}
                ), 'get_fields', setter='set_fields')
    from_to_stock = fields.Function(fields.Many2One('ekd.company.department.stock',
                'Stock',
                states={'invisible': Equal(Eval('direct_document'),'move_goods')}
                ), 'get_fields', setter='set_fields')
    from_stock_fnc = fields.Function(fields.Many2One('ekd.company.department.stock',
                'From Stock',
                states={'invisible': Not(Equal(Eval('direct_document'),'move_goods'))}
                ), 'get_fields', setter='set_fields')
    to_stock_fnc = fields.Function(fields.Many2One('ekd.company.department.stock',
                'To Stock',
                states={'invisible': Not(Equal(Eval('direct_document'),'move_goods'))}
                ), 'get_fields', setter='set_fields')
    shipper = fields.Many2One('party.party', 'Shipper',
                states={'invisible': Eval('as_shipper', True)})
    consignee = fields.Many2One('party.party', 'Consignee',
                states={'invisible': Eval('as_consignee', True)})
    as_shipper = fields.Boolean('As Shipper')
    as_consignee = fields.Boolean('As Consignee')

    amount_doc = fields.Function(fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2))), 
                'get_fields', setter='set_fields')
    amount_tax = fields.Function(fields.Numeric('Amount Tax', digits=(16, Eval('currency_digits', 2))), 
                'get_fields')
    currency = fields.Many2One('currency.currency', 'Currency')
    currency_digits = fields.Function(fields.Integer('Currency Digits',
                on_change_with=['currency']), 'get_currency_digits')
    type_tax = fields.Selection([
            ('not_vat','Not VAT'),
            ('including','Including'),
            ('over_amount','Over Amount')
            ], 'Type Compute Tax')
    lines = fields.One2Many('ekd.document.line.product', 'invoice', 'Lines',
                context={'type_product':Eval('type_product')})
    state_doc = fields.Function(fields.Selection(_STATE_INVOICE_GOODS, required=True, readonly=True, string='State'), 'get_fields', 
                setter='set_fields')
    deleting = fields.Boolean('Flag deleting', readonly=True)

    def __init__(self):
        super(DocumentInvoiceGoods, self).__init__()
        self._rpc.update({
            'button_add_number': True,
            'button_draft': True,
            'button_to_pay': True,
            'button_part_paid': True,
            'button_paid': True,
            'button_received': True,
            'button_cancel': True,
            'button_restore': True,
            'button_print': True,
            'button_send': True,
            'button_post': True,
            'button_obtained': True,
            'button_delivered': True,
            })

        self._order.insert(0, ('date_document', 'ASC'))
        self._order.insert(1, ('template', 'ASC'))

    def default_state_doc(self):
        return Transaction().context.get('state') or 'draft'

    def default_as_shipper(self):
        return True

    def default_as_consignee(self):
        return True

    def default_type_product(self):
        return Transaction().context.get('type_product') or 'material'

    def default_from_to_party_doc(self):
        return Transaction().context.get('from_to_party') or False

    def default_direct_document(self):
        return Transaction().context.get('direct_document') or 'invoice_revenue'

    def default_template_invoice(self):
        context = Transaction().context
        if context.get('template_invoice', False):
            return  context.get('template_invoice')
        else:
            template_obj = self.pool.get('ekd.document.template')
            template_ids = template_obj.search( [
                        ('type_account','=',context.get('direct_document'))
                        ], order=[('sequence','ASC')])
            if len(template_ids) > 0:
                return template_ids[0]

    def default_currency(self):
        company_obj = self.pool.get('company.company')
        currency_obj = self.pool.get('currency.currency')
        context = Transaction().context
        if context.get('company'):
            company = company_obj.browse( context['company'])
            return company.currency.id
        return False

    def default_currency_digits(self):
        company_obj = self.pool.get('company.company')
        context = Transaction().context
        if context.get('company'):
            company = company_obj.browse( context['company'])
            return company.currency.digits
        return 2

    def default_state_doc(self):
        return Transaction().context.get('state_doc', 'draft')

    def get_currency_digits(self,  ids, name):
        res = {}
        for line in self.browse( ids):
            res[line.id] = line.currency and line.currency.digits or 2
        return res

    def default_amount(self):
        return Transaction().context.get('amount') or Decimal('0.0')

    def default_company(self):
        return Transaction().context.get('company') or False

    def documents_base_get(self):
        dictions_obj = self.pool.get('ir.dictions')
        res = []
        diction_ids = dictions_obj.search([
            ('model', '=', 'ekd.document.head.invoice_goods'),
            ('pole', '=', 'document_base'),
            ], order=[('sequence','ASC')])
        for diction in dictions_obj.browse(diction_ids):
            res.append([diction.key, diction.value])
        return res

    def get_fields(self,  ids, names):
        if not ids:
            return {}
        res={}
        for line in self.browse( ids):
            for name in names:
                res.setdefault(name, {})
                res[name].setdefault(line.id, False)
                if name == 'number_doc':
                    if line.direct_document == 'input_goods' :
                        res[name][line.id] = line.number_in
                    else:
                        res[name][line.id] = line.number_our
                elif name == 'state_doc':
                        res[name][line.id] = line.state
                elif name == 'amount_doc':
                    for line_spec in line.lines:
                        if line_spec.type == 'line':
                            res[name][line.id] += line_spec.amount
                elif name == 'amount_tax':
                    for line_spec in line.lines:
                        if line_spec.type == 'line':
                            res[name][line.id] += line_spec.amount_tax
                elif name == 'from_to_party':
                    if line.direct_document == 'input_goods' :
                        res[name][line.id] = line.from_party.id
                    else:
                        res[name][line.id] = line.to_party.id
                elif name == 'from_party_fnc':
                    res[name][line.id] = line.from_party.id
                elif name == 'to_party_fnc':
                    res[name][line.id] = line.to_party.id
                elif name == 'template_invoice':
                    res[name][line.id] = line.template.id
                elif name == 'note_cash':
                    res[name][line.id] = line.note
        return res

    def set_fields(self,  ids, name, value):
        if isinstance(ids, list):
            ids = ids[0]
        if not value:
            return
        document = self.browse( ids)
        if name == 'state_doc':
            self.write( ids, {'state':value, })
        elif name == 'template_invoice':
            self.write( ids, { 'template': value, })
        elif name == 'number_doc':
            if document.direct_document == 'input_goods':
                self.write( ids, { 'number_in': value, })
            else:
                self.write( ids, { 'number_our': value, })
        elif name == 'amount_doc':
            self.write( ids, { 'amount': value, })
        elif name == 'from_party_doc':
            if document.direct_document == 'input_goods':
                self.write( ids, { 'from_party': value, })
            else:
                self.write( ids, { 'to_party': value, })
        elif name == 'from_party_fnc':
            self.write( ids, { 'from_party': value, })
        elif name == 'to_party_fnc':
            self.write( ids, { 'to_party': value, })

    def template_select_get(self):
        context = Transaction().context
        template_obj = self.pool.get('ekd.document.template')
        raise Exception(str(context))
        template_ids = template_obj.search( ['type_account','=', 
                        context.get('direct_document', 'input_goods')])
        res=[]
        for template in template_obj.browse( template_ids):
            res.append([template.id,template.name])
        return res

    def template_search(self,  name, domain=[]):
        if name == 'template_bank':
            for table, exp, value in domain:
                return  [('template', 'ilike', value)]
        elif name == 'from_party_doc':
            document_obj = self.pool.get('ekd.document')
            table, exp, value = domain[0]
            find_ids = document_obj.search( [('from_party', 'ilike', value)])
            return [('document', 'in', find_ids)]
        elif name == 'to_party_doc':
            document_obj = self.pool.get('ekd.document')
            table, exp, value = domain[0]
            find_ids = document_obj.search( [('to_party', 'ilike', value)])
            return [('document', 'in', find_ids)]

    def get_rec_name(self,  ids, name):
        if not ids:
            return {}
        return self.pool.get('ekd.document').get_rec_name( ids, name)

    def on_change_document_base(self, vals):
        if not vals.get('document_base'):
            return {}
        model, model_id = vals.get('document_base').split(',')
        model_obj = self.pool.get(model)
        if model_id == '0':
            return {}
        model_line = model_obj.browse(int(model_id))
        lines_new = {}
        field_import = [
        'product_ref', 
        'type_tax', 
        'tax', 
        'type', 
        'currency', 
        'unit', 
        'currency_digits', 
        'unit_price', 
        'amount_tax', 
        'product', 
        'description', 
        'type_product', 
        'unit_digits', 
        'amount', 
        'quantity']

        if hasattr(model_obj, 'lines'):
            for line_new in model_line.lines:
                if line_new.type == 'line':
                    tmp_line = line_new.read(line_new.id, field_import)
                    tmp_line['state'] = 'draft'
                    lines_new.setdefault('add', []).append(tmp_line)
            return {
                'from_to_party': model_line.from_to_party.id,
                'parent': int(model_id),
                'lines':lines_new}
        else:
            return {
                'from_to_party': model_line.from_to_party.id,
                'parent': int(model_id)}

    def create(self, vals):
        later = {}
        vals = vals.copy()
        cursor = Transaction().cursor
        for field in vals:
            if field in self._columns\
                    and hasattr(self._columns[field], 'set'):
                later[field] = vals[field]
        for field in later:
            del vals[field]
        if cursor.nextid(self._table):
            cursor.setnextid(self._table, cursor.currid(self._table))
        new_id = super(DocumentInvoiceGoods, self).create( vals)
        invoice = self.browse( new_id)
        new_id = invoice.document.id
        cr = Transaction().cursor
        cr.execute('UPDATE "' + self._table + '" SET id = %s '\
                        'WHERE id = %s', (invoice.document.id, invoice.id))
        ModelStorage.delete(self, invoice.id)
        self.write( new_id, later)
        res = self.browse( new_id)
        return res.id

    def delete(self, ids):
        cr = Transaction().cursor
        doc_lines_obj = self.pool.get('ekd.document.line.product')
        for document in self.browse(ids):
            if document.state == 'deleted' and document.deleting:
                return super(DocumentInvoiceGoods, self).delete(ids)
            else:
                doc_lines_obj.write([x.id for x in document.lines], {'state': 'deleted', 'deleting':True})
                self.write(document.id, {'state': 'deleted', 'deleting':True})
        return True

    def button_add_number(self, ids):
        return self.new_number(ids)

    def button_post(self, ids):
        return self.post(ids)

    def button_obtained(self, ids):
        return self.obtained(ids)

    def button_delivered(self, ids):
        return self.delivered(ids)

    def button_paid(self, ids):
        return self.paid(ids)

    def button_part_paid(self, ids):
        return self.part_paid(ids)

    def button_to_pay(self, ids):
        return self.to_pay(ids)

    def button_send(self, ids):
        return self.send(ids)

    def button_print(self, ids):
        return self.print_document(ids)

    def button_cancel(self, ids):
        return self.cancel(ids)

    def button_draft(self, ids):
        return self.draft(ids)

    def button_restore(self, ids):
        return self.draft(ids)

    def new_number(self, ids):
        sequence_obj = self.pool.get('ir.sequence')
        for document in self.browse(ids):
            if document.template and document.template.sequence:
                sequence = sequence_obj.get_id(document.template.sequence.id)
            else:
                raise Exception('Error', 'Sequence for document not find!')
            self.write(document.id, {
                    'number_doc': sequence,
                    })

    def post(self, ids):
        return self.write(ids, {
            'state': 'posted',
            })

    def obtained(self, ids):
        return self.write(ids, {
            'state': 'obtained',
            })

    def delivered(self, ids):
        return self.write(ids, {
            'state': 'delivered',
            })

    def send(self, ids):
        return self.write(ids, {
            'state': 'sended',
            })

    def to_pay(self, ids):
        return self.write(ids, {
            'state': 'to_pay',
            })

    def paid(self, ids):
        return self.write(ids, {
            'state': 'paid',
            })

    def part_paid(self, ids):
        return self.write(ids, {
            'state': 'part_paid',
            })

    def print_document(self, ids):
        return self.write(ids, {
            'state': 'printed',
            })

    def draft(self, ids):
        return self.write(ids, {
                'state': 'draft',
                'deleting':False
                })

    def cancel(self, ids):
        return self.write(ids, {
                'state': 'canceled',
                })
Ejemplo n.º 23
0
class Invoice:
    __metaclass__ = PoolMeta
    __name__ = 'account.invoice'
    payment_type_kind = fields.Function(
        fields.Selection(
            [
                ('payable', 'Payable'),
                ('receivable', 'Receivable'),
            ],
            'Kind of payment type',
            states={
                'invisible': True,
            },
        ), 'on_change_with_payment_type_kind')
    payment_type = fields.Many2One(
        'account.payment.type',
        'Payment Type',
        domain=[
            ('kind', '=', Eval('payment_type_kind')),
        ],
        states={
            'readonly': Not(Bool(Eval('state').in_(['draft', 'validated']))),
        },
        depends=['payment_type_kind', 'state'])

    @fields.depends('type', 'untaxed_amount', 'lines')
    def on_change_with_payment_type_kind(self, name=None):
        if self.untaxed_amount:
            if self.type == 'out':
                if self.untaxed_amount >= ZERO:
                    return 'receivable'
                else:
                    return 'payable'
            elif self.type == 'in':
                if self.untaxed_amount >= ZERO:
                    return 'payable'
                else:
                    return 'receivable'
        return 'receivable' if self.type == 'out' else 'payable'

    @fields.depends('party',
                    'company',
                    'type',
                    'untaxed_amount',
                    'lines',
                    'payment_type',
                    methods=['payment_type_kind'])
    def on_change_with_payment_type(self, name=None):
        kind = self.on_change_with_payment_type_kind()
        if (self.payment_type and self.payment_type.kind == kind):
            return self.payment_type.id
        if not self.untaxed_amount:
            return None
        if self.party:
            if self.type == 'out':
                if (self.untaxed_amount >= ZERO
                        and self.party.customer_payment_type):
                    return self.party.customer_payment_type.id
                elif (self.untaxed_amount < ZERO
                      and self.party.supplier_payment_type):
                    return self.party.supplier_payment_type.id
            elif self.type == 'in':
                if (self.untaxed_amount >= ZERO
                        and self.party.supplier_payment_type):
                    return self.party.supplier_payment_type.id
                elif (self.untaxed_amount < ZERO
                      and self.party.customer_payment_type):
                    return self.party.customer_payment_type.id

        if self.company:
            if self.type == 'out':
                if (self.untaxed_amount >= ZERO
                        and self.company.party.customer_payment_type):
                    return self.company.party.customer_payment_type.id
                elif (self.untaxed_amount < ZERO
                      and self.company.party.supplier_payment_type):
                    return self.company.party.supplier_payment_type.id
            elif self.type == 'in':
                if (self.untaxed_amount >= ZERO
                        and self.company.party.supplier_payment_type):
                    return self.company.party.supplier_payment_type.id
                elif (self.untaxed_amount < ZERO
                      and self.company.party.customer_payment_type):
                    return self.company.party.customer_payment_type.id
        return None

    @classmethod
    def post(cls, invoices):
        pool = Pool()
        Date = pool.get('ir.date')
        Payment = pool.get('account.payment')
        PayLine = pool.get('account.move.line.pay', type='wizard')
        super(Invoice, cls).post(invoices)
        lines_to_pay = defaultdict(list)
        for invoice in invoices:
            if (invoice.payment_type and invoice.payment_type.payment_journal):
                for line in invoice.lines_to_pay:
                    key = (invoice.payment_type, line.maturity_date)
                    lines_to_pay[key].append(line.id)

        to_approve = []
        for key, lines in lines_to_pay.iteritems():
            payment_type, date = key
            session_id, _, _ = PayLine.create()
            payline = PayLine(session_id)
            payline.start.journal = payment_type.payment_journal
            payline.start.date = date or Date.today()
            payline.start.approve = True
            with Transaction().set_context(active_ids=lines):
                action, data = payline.do_pay(None)
            if payment_type.approve_payments:
                to_approve.extend(data['res_id'])
            PayLine.delete(session_id)
        if to_approve:
            Payment.approve(Payment.browse(to_approve))
Ejemplo n.º 24
0
# -*- encoding: utf-8 -*-
#NOTE: Заявки на деньги
"Document Request for money"
from trytond.model import ModelView, ModelSQL, ModelStorage, fields
from trytond.transaction import Transaction
from trytond.report import Report
from trytond.tools import safe_eval
import time
from decimal import Decimal, ROUND_HALF_EVEN
from trytond.pyson import In, If, Get, Eval, Not, Equal, Bool, Or, And
import time
import datetime
from ekd_state_document import _STATE_REQUEST

_REQUEST_STATES = {
    'readonly': Not(In(Eval('state_doc'), ['empty', 'draft'])),
}

_REQUEST_DEPENDS = ['state_doc']

_LINE_STATES = {
    'readonly': Equal(Eval('state_doc'), 'draft'),
}

_LINE_DEPENDS = ['state_doc']

_ISSUE_STATES = {
    'readonly':
    Not(In(Eval('state_doc'), ['empty', 'draft', 'confirmed', 'payment'])),
}
Ejemplo n.º 25
0
    ('spouse', 'spouse'),
    ('sibling', 'sibling'),
    ('grandparent', 'grandchild'),
    ('auntuncle', 'niecenephew'),
    ('cousin', 'cousin'),
    ('friend', 'friend'),
    ('coworker', 'coworker'),
    ('girlboyfriend', 'girlboyfriend'),
    ('employer', 'employee'),
    ('other', 'other'),
]

NNre = re.compile('(%?)NN-(.*)', re.I)

_STATES = {
    'invisible': Not(Bool(Eval('is_person'))),
}


class PartyPatient(ModelSQL, ModelView):
    'Party'
    __name__ = 'party.party'

    upi = fields.Function(fields.Char('UPI', help='Unique Party Identifier'),
                          'get_upi_display', searcher='search_upi')
    firstname = fields.Char('First name', states=_STATES, depends=_DEPENDS,
                            select=True)
    middlename = fields.Char('Middle Name', states=_STATES, depends=_DEPENDS,
                             help="Middle name or names of Patient")
    maiden_name = fields.Char(
        'Maiden Name',
Ejemplo n.º 26
0
class DocumentRequestLine(ModelSQL, ModelView):
    "Document specifications of Request"
    _name = 'ekd.document.line.request'
    _description = __doc__

    requestcash = fields.Many2One('ekd.document.head.request',
                                  'Document Request',
                                  ondelete='CASCADE',
                                  select=1)
    type = fields.Selection([
        ('line', 'Line'),
        ('subtotal', 'Subtotal'),
    ],
                            'Type',
                            select=1,
                            required=True,
                            states={
                                'invisible': Bool(Eval('standalone')),
                            })
    name = fields.Char('Description',
                       states=_LINE_STATES,
                       depends=_LINE_DEPENDS)
    note = fields.Text('Note', states=_LINE_STATES, depends=_LINE_DEPENDS)
    product = fields.Many2One('product.product',
                              'Product',
                              states=_LINE_STATES,
                              depends=_LINE_DEPENDS)
    uom = fields.Many2One('product.uom',
                          'Unit',
                          states={
                              'required': Bool(Eval('product')),
                              'readonly': Not(Equal(Eval('state'), 'draft'))
                          },
                          domain=[('category', '=',
                                   (Eval('product'),
                                    'product.default_uom.category'))],
                          context={
                              'category':
                              (Eval('product'), 'product.default_uom.category')
                          })
    quantity = fields.Float('Quantity',
                            digits=(16, Eval('currency_digits', 2)),
                            states=_LINE_STATES,
                            depends=_LINE_DEPENDS)
    unit_digits = fields.Function(
        fields.Integer('Unit Digits', on_change_with=['uom']), 'get_fields')
    unit_price = fields.Numeric('Price Unit',
                                digits=(16, Eval('currency_digits', 2)),
                                states=_LINE_STATES,
                                depends=_LINE_DEPENDS)
    lines_received = fields.One2Many('ekd.document.line.payment',
                                     'line_request', 'Line Amount Received')
    amount_request = fields.Numeric('Amount Request',
                                    digits=(16, Eval('currency_digits', 2)),
                                    states=_LINE_STATES,
                                    depends=_LINE_DEPENDS)
    amount_received = fields.Function(
        fields.Numeric('Amount Received',
                       digits=(16, Eval('currency_digits', 2))), 'get_fields')
    amount_balance = fields.Function(
        fields.Numeric('Balance',
                       digits=(16, Eval('currency_digits', 2)),
                       readonly=True,
                       on_change_with=['amount_request', 'amount_received']),
        'get_fields')
    amount_payment = fields.Function(fields.Numeric(
        'Amount Payment', digits=(16, Eval('currency_digits', 2))),
                                     'get_fields',
                                     setter='set_fields')
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
                                      'get_currency_digits')
    state = fields.Selection([
        ('draft', 'Draft'),
        ('request', 'Request'),
        ('confirmed', 'Confirmed'),
        ('payment', 'Payment'),
        ('partially', 'Partially'),
        ('fully', 'Fully'),
        ('done', 'Done'),
        ('deleted', 'Deleted'),
    ],
                             'State',
                             readonly=True)

    def default_state(self):
        return 'draft'

    def default_type(self):
        return 'line'

    def default_currency_digits(self):
        return Transaction().context.get('currency_digits', 2)

    def get_fields(self, ids, names):
        if not ids:
            return {}
        res = {}
        for request in self.browse(ids):
            for name in names:
                res.setdefault(name, {})
                res[name].setdefault(request.id, {})
                if name == 'amount_balance':
                    res[name][
                        request.
                        id] = request.amount_request - request.amount_received
                elif name == 'unit_digits':
                    if request.uom:
                        res[name][request.id] = request.uom.digits
                    else:
                        res[name][request.id] = 2
                elif name == 'amount_payment':
                    amount_payment = Decimal('0.0')
                    for line_received in request.lines_received:
                        if line_received.state == 'payment':
                            amount_payment += line_received.amount_payment
                    res[name][request.id] = amount_payment


#                    res[name][request.id] = request.amount_request-request.amount_received
                elif name == 'amount_received':
                    amount_received = Decimal('0.0')
                    for line_received in request.lines_received:
                        if line_received.state == 'done':
                            amount_received += line_received.amount_payment
                    res[name][request.id] = amount_received
        return res

    def set_fields(self, id, name, value):
        if not value:
            return

    def get_currency_digits(self, ids, name):
        res = {}
        for line in self.browse(ids):
            res[line.
                id] = line.requestcash.currency and line.requestcash.currency.digits or 2
        return res

    def on_change_with_amount_balance(self, vals):
        if vals.get('amount_request') and vals.get('amount_received'):
            return vals.get('amount_request') - vals.get('amount_received')
        elif vals.get('amount_request'):
            return vals.get('amount_request')
        else:
            return Decimal('0.0')
Ejemplo n.º 27
0
class PartyRelative(ModelSQL, ModelView):
    'Relative/NOK/Employer'
    __name__ = 'party.relative'

    party = fields.Many2One('party.party', 'Patient', required=True,
                            ondelete='CASCADE')
    relative = fields.Many2One(
        'party.party', 'Relative', ondelete='CASCADE',
        domain=[('id', '!=', Eval('party'))],
        states={'invisible': Not(Eval('is_party', False)),
                'required': Eval('is_party', False),
                'readonly': Eval('id', 0) > 0})
    relationship = fields.Selection(RELATIONSHIP_LIST, 'Relationship',
                                    help='Relationship of contact to patient')
    
    relationship_display = fields.Function(fields.Char('Relationship'),
                                           'get_relationship_display')
    relative_summary = fields.Function(fields.Text('Relative Summary',
                                       states={'invisible': Or(Eval('id', 0) <= 0,
                                                               ~Eval('is_party', False))}),
                                       'get_relative_val')
    phone_number = fields.Function(fields.Char('Phone/Mobile'),
                                   'get_relative_val')

    party_relative_summary = fields.Function(fields.Text('Relative Summary'),
                                             'get_relative_val')
    party_phone_number = fields.Function(fields.Char('Phone/Mobile'),
                                         'get_relative_val')
    relationship_reverse = fields.Function(fields.Selection(RELATIONSHIP_LIST,
                                           'Relationship'), 'get_reverse_relationship')
    is_party = fields.Boolean('Relative is a party',
                              help='Check to select the party that represents'
                              ' this relative',
                              states={'readonly': Eval('id', 0) > 0})
    relative_name = fields.Function(fields.Char('Relative'), 'get_relative_name',
                                    searcher='search_relative_name')
    relative_lastname = fields.Char('Last name', states=NOTPARTY_RSTATES)
    relative_firstname = fields.Char('Given name(s)', states=NOTPARTY_RSTATES)
    relative_sex = fields.Selection([(None, '')] + SEX_OPTIONS, 'Sex',
                                    states=NOTPARTY_RSTATES)
    relative_country = fields.Many2One('country.country', 'Country',
                                       states=NOTPARTY_STATES)
    relative_state = fields.Many2One('country.subdivision', 'Parish/Province',
                                     domain=[('country', '=', Eval('relative_country'))],
                                     states=NOTPARTY_STATES)
    relative_address = fields.Text('Address details', states=NOTPARTY_STATES)
    relative_phone = fields.Char('Phone/Mobile number(s)',
                                 states=NOTPARTY_STATES,
                                 help='Separate multiple numbers with comma (,)')

    @staticmethod
    def default_is_party():
        return False

    @staticmethod
    def default_relative_country():
        country = Pool().get('country.country')
        jm, = country.search([('code', '=', 'JM')])
        return jm.id


    def get_relative_val(self, field_name):
        if field_name in ['phone_number', 'party_phone_number',
                          'relative_summary', 'party_relative_summary']:
            # return a formatted text summary for the NOK
            # to include phone(s), email, address[0]
            sexdict = dict([(None, '')] + SEX_OPTIONS)
            if field_name.startswith('party'):
                partyobj = self.party
            elif self.is_party:
                partyobj = self.relative
            else:
                # work out the required field using the internal fields
                if 'phone_number' in field_name:
                    return self.relative_phone
                else:
                    # summary, we'll include sex and address
                    rdata = [('Sex', sexdict.get(self.relative_sex, '')),
                             ('Address', '')]
                    if self.relative_address and self.relative_state and self.relative_country:
                        rdata.append(
                            ('', '%s\n%s, %s' % (self.relative_address,
                             self.relative_state.name,
                             self.relative_country.name)))
                    partyobj = False
                    v = '\n'.join([': '.join(x) for x in rdata])
            if partyobj:
                rdata = []
                contact_types = ['phone', 'mobile', 'email']
                for mech in partyobj.contact_mechanisms:
                    if mech.type in contact_types:
                        rdata.append((mech.type.capitalize(), mech.value))
                if 'phone_number' in field_name:
                    v = ', '.join([y for x,y in rdata if x in ['Phone', 'Mobile']])
                else:
                    rdata.insert(0, ('Sex', sexdict.get(partyobj.sex, '')))
                    num_addresses = len(partyobj.addresses) - 1
                    if num_addresses >= 0:
                        rdata.append(('Address', ''))
                        rdata.append(('', partyobj.addresses[0].simple_address))
                        if num_addresses:
                            rdata.append(('...', '(and %d more)' % num_addresses))
                    v = '\n'.join([': '.join(x) for x in rdata])
        else:
            # return the singular value from the NOK if we can
            try:
                v = getattr(self.relative, field_name)
            except AttributeError, e:
                print repr(e)
                v = None
        return v
Ejemplo n.º 28
0
METRIC_CONV = {'length': (1 / 2.54), 'weight': 2.20462262}

URINALYSIS = {
    'default': [('neg', 'negative'), ('trace', 'trace'), ('+', '+'),
                ('++', '2+'), ('+++', '3+'), ('++++', '4+'), ('+++++', '5+')],
    'nitrite': [('neg.', 'negative'), ('trace', 'trace'), ('small', 'small'),
                ('moderate', 'moderate'), ('large', 'large'),
                ('large+', 'large+')]
}

PYSON_MALE = Equal(Eval('encounter.patient.sex', ''), 'm'),
PYSON_FEMALE = Equal(Eval('encounter.patient.sex', ''), 'f'),
PYSON_MENARCH = Eval('encounter.patient.childbearing_age', False)
STATE_NO_MENSES = {
    'readonly': Not(Eval('childbearing_age', True)),
    'invisible': Not(Eval('childbearing_age', True))
}
for key in STATE_NO_MENSES:
    if key in SIGNED_STATES:
        STATE_NO_MENSES[key] = Or(STATE_NO_MENSES[key], SIGNED_STATES[key])


class EncounterAnthro(BaseComponent):
    'Anthropometry'
    __name__ = 'gnuhealth.encounter.anthropometry'

    weight = fields.Float('Weight (kg)',
                          help='Weight in Kilos',
                          states=SIGNED_STATES)
    height = fields.Float('Height (cm)',
Ejemplo n.º 29
0
class ApacheII(ModelSQL, ModelView):
    'Apache II scoring'
    __name__ = 'gnuhealth.icu.apache2'

    name = fields.Many2One('gnuhealth.inpatient.registration',
                           'Registration Code',
                           required=True)
    score_date = fields.DateTime('Date',
                                 help="Date of the score",
                                 required=True)

    age = fields.Integer('Age', help='Patient age in years')
    temperature = fields.Float('Temperature', help='Rectal temperature')
    mean_ap = fields.Integer('MAP', help='Mean Arterial Pressure')
    heart_rate = fields.Integer('Heart Rate')
    respiratory_rate = fields.Integer('Respiratory Rate')
    fio2 = fields.Float('FiO2')
    pao2 = fields.Integer('PaO2')
    paco2 = fields.Integer('PaCO2')
    aado2 = fields.Integer('A-a DO2')

    ph = fields.Float('pH')
    serum_sodium = fields.Integer('Sodium')
    serum_potassium = fields.Float('Potassium')
    serum_creatinine = fields.Float('Creatinine')
    arf = fields.Boolean('ARF', help='Acute Renal Failure')
    wbc = fields.Float('WBC',
                       help="White blood cells x 1000 - if you"
                       " want to input 4500 wbc / ml, type in 4.5")
    hematocrit = fields.Float('Hematocrit')
    gcs = fields.Integer(
        'GSC',
        help='Last Glasgow Coma Scale'
        ' You can use the GSC calculator from the Patient Evaluation Form.')
    chronic_condition = fields.Boolean(
        'Chronic condition', help='Organ Failure or immunocompromised patient')
    hospital_admission_type = fields.Selection(
        [(None, ''), ('me', 'Medical or emergency postoperative'),
         ('el', 'elective postoperative')],
        'Hospital Admission Type',
        states={
            'invisible': Not(Bool(Eval('chronic_condition'))),
            'required': Bool(Eval('chronic_condition'))
        },
        sort=False)

    apache_score = fields.Integer('Score')

    #Default FiO2 PaO2 and PaCO2 so we do the A-a gradient
    #calculation with non-null values

    @fields.depends('fio2', 'pao2', 'paco2')
    def on_change_with_aado2(self):
        # Calculates the Alveolar-arterial difference
        # based on FiO2, PaCO2 and PaO2 values
        if (self.fio2 and self.paco2 and self.pao2):
            return (713 * self.fio2) - (self.paco2 / 0.8) - self.pao2

    @fields.depends('age', 'temperature', 'mean_ap', 'heart_rate',
                    'respiratory_rate', 'fio2', 'pao2', 'aado2', 'ph',
                    'serum_sodium', 'serum_potassium', 'serum_creatinine',
                    'arf', 'wbc', 'hematocrit', 'gcs', 'chronic_condition',
                    'hospital_admission_type')
    def on_change_with_apache_score(self):
        # Calculate the APACHE SCORE from the variables in the

        total = 0
        # Age
        if (self.age):
            if (self.age > 44 and self.age < 55):
                total = total + 2
            elif (self.age > 54 and self.age < 65):
                total = total + 3
            elif (self.age > 64 and self.age < 75):
                total = total + 5
            elif (self.age > 74):
                total = total + 6

        # Temperature
        if (self.temperature):
            if ((self.temperature >= 38.5 and self.temperature < 39)
                    or (self.temperature >= 34 and self.temperature < 36)):
                total = total + 1
            elif (self.temperature >= 32 and self.temperature < 34):
                total = total + 2
            elif ((self.temperature >= 30 and self.temperature < 32)
                  or (self.temperature >= 39 and self.temperature < 41)):
                total = total + 3
            elif (self.temperature >= 41 or self.temperature < 30):
                total = total + 4

        # Mean Arterial Pressure (MAP)
        if (self.mean_ap):
            if ((self.mean_ap >= 110 and self.mean_ap < 130)
                    or (self.mean_ap >= 50 and self.mean_ap < 70)):
                total = total + 2
            elif (self.mean_ap >= 130 and self.mean_ap < 160):
                total = total + 3
            elif (self.mean_ap >= 160 or self.mean_ap < 50):
                total = total + 4

        # Heart Rate
        if (self.heart_rate):
            if ((self.heart_rate >= 55 and self.heart_rate < 70)
                    or (self.heart_rate >= 110 and self.heart_rate < 140)):
                total = total + 2
            elif ((self.heart_rate >= 40 and self.heart_rate < 55)
                  or (self.heart_rate >= 140 and self.heart_rate < 180)):
                total = total + 3
            elif (self.heart_rate >= 180 or self.heart_rate < 40):
                total = total + 4

        # Respiratory Rate
        if (self.respiratory_rate):
            if ((self.respiratory_rate >= 10 and self.respiratory_rate < 12) or
                (self.respiratory_rate >= 25 and self.respiratory_rate < 35)):
                total = total + 1
            elif (self.respiratory_rate >= 6 and self.respiratory_rate < 10):
                total = total + 2
            elif (self.respiratory_rate >= 35 and self.respiratory_rate < 50):
                total = total + 3
            elif (self.respiratory_rate >= 50 or self.respiratory_rate < 6):
                total = total + 4

        # FIO2
        if (self.fio2):
            # If Fi02 is greater than 0.5, we measure the AaDO2 gradient
            # Otherwise, we take into account the Pa02 value

            if (self.fio2 >= 0.5):
                if (self.aado2 >= 200 and self.aado2 < 350):
                    total = total + 2

                elif (self.aado2 >= 350 and self.aado2 < 500):
                    total = total + 3

                elif (self.aado2 >= 500):
                    total = total + 4

            else:
                if (self.pao2 >= 61 and self.pao2 < 71):
                    total = total + 1

                elif (self.pao2 >= 55 and self.pao2 < 61):
                    total = total + 3

                elif (self.pao2 < 55):
                    total = total + 4

        # Arterial pH
        if (self.ph):
            if (self.ph >= 7.5 and self.ph < 7.6):
                total = total + 1
            elif (self.ph >= 7.25 and self.ph < 7.33):
                total = total + 2
            elif ((self.ph >= 7.15 and self.ph < 7.25)
                  or (self.ph >= 7.6 and self.ph < 7.7)):
                total = total + 3
            elif (self.ph >= 7.7 or self.ph < 7.15):
                total = total + 4

        # Serum Sodium
        if (self.serum_sodium):
            if (self.serum_sodium >= 150 and self.serum_sodium < 155):
                total = total + 1
            elif ((self.serum_sodium >= 155 and self.serum_sodium < 160)
                  or (self.serum_sodium >= 120 and self.serum_sodium < 130)):
                total = total + 2
            elif ((self.serum_sodium >= 160 and self.serum_sodium < 180)
                  or (self.serum_sodium >= 111 and self.serum_sodium < 120)):
                total = total + 3
            elif (self.serum_sodium >= 180 or self.serum_sodium < 111):
                total = total + 4

        # Serum Potassium
        if (self.serum_potassium):
            if ((self.serum_potassium >= 3 and self.serum_potassium < 3.5) or
                (self.serum_potassium >= 5.5 and self.serum_potassium < 6)):
                total = total + 1
            elif (self.serum_potassium >= 2.5 and self.serum_potassium < 3):
                total = total + 2
            elif (self.serum_potassium >= 6 and self.serum_potassium < 7):
                total = total + 3
            elif (self.serum_potassium >= 7 or self.serum_potassium < 2.5):
                total = total + 4

        # Serum Creatinine
        if (self.serum_creatinine):
            arf_factor = 1
            if (self.arf):
                # We multiply by 2 the score if there is concomitant ARF
                arf_factor = 2
            if ((self.serum_creatinine < 0.6) or
                (self.serum_creatinine >= 1.5 and self.serum_creatinine < 2)):
                total = total + 2 * arf_factor
            elif (self.serum_creatinine >= 2 and self.serum_creatinine < 3.5):
                total = total + 3 * arf_factor
            elif (self.serum_creatinine >= 3.5):
                total = total + 4 * arf_factor

        # Hematocrit
        if (self.hematocrit):
            if (self.hematocrit >= 46 and self.hematocrit < 50):
                total = total + 1
            elif ((self.hematocrit >= 50 and self.hematocrit < 60)
                  or (self.hematocrit >= 20 and self.hematocrit < 30)):
                total = total + 2
            elif (self.hematocrit >= 60 or self.hematocrit < 20):
                total = total + 4

        # WBC ( x 1000 )
        if (self.wbc):
            if (self.wbc >= 15 and self.wbc < 20):
                total = total + 1
            elif ((self.wbc >= 20 and self.wbc < 40)
                  or (self.wbc >= 1 and self.wbc < 3)):
                total = total + 2
            elif (self.wbc >= 40 or self.wbc < 1):
                total = total + 4

        # Immnunocompromised or severe organ failure
        if (self.chronic_condition):
            if (self.hospital_admission_type == 'me'):
                total = total + 5
            else:
                total = total + 2

        return total
Ejemplo n.º 30
0
# -*- encoding: utf-8 -*-.
#This file is part of Tryton.  The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
'Address'
from trytond.model import ModelView, ModelSQL, fields
from trytond.transaction import Transaction
from trytond.pyson import If, Greater, Eval, Not, Bool, Eval

STATES = {
    'readonly': Not(Bool("active")),
}


class Bank(ModelSQL, ModelView):
    "Bank"
    _name = 'ekd.party.bank'
    _description = __doc__

    name = fields.Char('Name', size=None, states=STATES)
    shortname = fields.Char('ShortName', size=None, states=STATES)
    city = fields.Char('City', size=None, states=STATES)
    zip = fields.Char('Zip', size=6, states=STATES)
    phone = fields.Char('Phones', size=None, states=STATES)
    address = fields.Char('Address', size=None, states=STATES)
    rkc = fields.Char('RKC', size=9, states=STATES)
    okpo = fields.Char('OKPO', size=8, states=STATES)
    corr_account = fields.Char('Corr Account', size=None, states=STATES)
    bic = fields.Char('BIC', size=9, states=STATES)
    start_date = fields.Date('Date Start')
    change_date = fields.Date('Date Change')
    end_date = fields.Date('Date End')