Esempio n. 1
0
class LineConsumption(ModelSQL, ModelView):
    "Subscription Line Consumption"
    __name__ = 'sale.subscription.line.consumption'

    line = fields.Many2One(
        'sale.subscription.line', "Line", required=True, select=True,
        ondelete='RESTRICT')
    quantity = fields.Float(
        "Quantity", digits=(16, Eval('unit_digits', 2)),
        depends=['unit_digits'])
    unit_digits = fields.Function(
        fields.Integer("Unit Digits"), 'on_change_with_unit_digits')
    date = fields.Date("Date", required=True)
    invoice_line = fields.Many2One(
        'account.invoice.line', "Invoice Line", readonly=True)

    @classmethod
    def __setup__(cls):
        super(LineConsumption, cls).__setup__()
        cls._order.insert(0, ('date', 'DESC'))
        cls._error_messages.update({
                'modify_invoiced_consumption': (
                    "You can not modify invoiced consumption."),
                'delete_invoiced_consumption': (
                    "You can not delete invoiced consumption."),
                'missing_account_revenue': ('Product "%(product)s" '
                    'misses a revenue account.'),
                })

    @fields.depends('line')
    def on_change_with_unit_digits(self, name=None):
        if self.line and self.line.unit:
            return self.line.unit.digits
        return 2

    @classmethod
    def copy(cls, consumptions, default=None):
        if default is None:
            default = {}
        else:
            default = default.copy()
        default.setdefault('invoice_line')
        return super(LineConsumption, cls).copy(consumptions, default=default)

    @classmethod
    def write(cls, *args):
        if any(c.invoice_line
                for consumptions in args[::2]
                for c in consumptions):
            cls.raise_user_error('modify_invoiced_consumption')
        super(LineConsumption, cls).write(*args)

    @classmethod
    def delete(cls, consumptions):
        if any(c.invoice_line for c in consumptions):
            cls.raise_user_error('delete_invoiced_consumption')
        super(LineConsumption, cls).delete(consumptions)

    @classmethod
    def get_invoice_lines(cls, consumptions, invoice):
        "Return a list of lines and a list of consumptions"
        pool = Pool()
        InvoiceLine = pool.get('account.invoice.line')

        lines, grouped_consumptions = [], []
        consumptions = sorted(consumptions, key=cls._group_invoice_key)
        for key, sub_consumptions in groupby(
                consumptions, key=cls._group_invoice_key):
            sub_consumptions = list(sub_consumptions)
            line = InvoiceLine(**dict(key))
            line.invoice_type = 'out'
            line.type = 'line'
            line.quantity = sum(c.quantity for c in sub_consumptions)

            line.account = line.product.account_revenue_used
            if not line.account:
                cls.raise_user_error('missing_account_revenue', {
                        'product': line.product.rec_name,
                        })

            taxes = []
            pattern = line._get_tax_rule_pattern()
            party = invoice.party
            for tax in line.product.customer_taxes_used:
                if party.customer_tax_rule:
                    tax_ids = party.customer_tax_rule.apply(tax, pattern)
                    if tax_ids:
                        taxes.extend(tax_ids)
                    continue
                taxes.append(tax.id)
            if party.customer_tax_rule:
                tax_ids = party.customer_tax_rule.apply(None, pattern)
                if tax_ids:
                    taxes.extend(tax_ids)
            line.taxes = taxes

            lines.append(line)
            grouped_consumptions.append(sub_consumptions)
        return lines, grouped_consumptions

    @classmethod
    def _group_invoice_key(cls, consumption):
        return (
            ('company', consumption.line.subscription.company),
            ('unit', consumption.line.unit),
            ('product', consumption.line.service.product),
            ('unit_price', consumption.line.unit_price),
            ('description', consumption.line.description),
            ('origin', consumption.line),
            )
Esempio n. 2
0
class Location(DeactivableMixin, tree(), ModelSQL, ModelView):
    "Stock Location"
    __name__ = 'stock.location'
    _default_warehouse_cache = Cache('stock.location.default_warehouse',
                                     context=False)

    name = fields.Char("Name", size=None, required=True, translate=True)
    code = fields.Char("Code",
                       size=None,
                       select=True,
                       help="The internal identifier used for the location.")
    address = fields.Many2One('party.address',
                              "Address",
                              states={
                                  'invisible': Eval('type') != 'warehouse',
                              })
    type = fields.Selection([
        ('supplier', 'Supplier'),
        ('customer', 'Customer'),
        ('lost_found', 'Lost and Found'),
        ('warehouse', 'Warehouse'),
        ('storage', 'Storage'),
        ('production', 'Production'),
        ('drop', 'Drop'),
        ('view', 'View'),
    ], "Location type")
    type_string = type.translated('type')
    parent = fields.Many2One("stock.location",
                             "Parent",
                             select=True,
                             left="left",
                             right="right",
                             help="Used to add structure above the location.")
    left = fields.Integer('Left', required=True, select=True)
    right = fields.Integer('Right', required=True, select=True)
    childs = fields.One2Many("stock.location",
                             "parent",
                             "Children",
                             help="Used to add structure below the location.")
    flat_childs = fields.Boolean(
        "Flat Children",
        help="Check to enforce a single level of children with no "
        "grandchildren.")
    warehouse = fields.Function(fields.Many2One('stock.location', 'Warehouse'),
                                'get_warehouse')
    input_location = fields.Many2One("stock.location",
                                     "Input",
                                     states={
                                         'invisible':
                                         Eval('type') != 'warehouse',
                                         'required':
                                         Eval('type') == 'warehouse',
                                     },
                                     domain=[
                                         ('type', '=', 'storage'),
                                         [
                                             'OR',
                                             ('parent', 'child_of',
                                              [Eval('id', -1)]),
                                             ('parent', '=', None),
                                         ],
                                     ],
                                     help="Where incoming stock is received.")
    output_location = fields.Many2One(
        "stock.location",
        "Output",
        states={
            'invisible': Eval('type') != 'warehouse',
            'required': Eval('type') == 'warehouse',
        },
        domain=[('type', '=', 'storage'),
                [
                    'OR', ('parent', 'child_of', [Eval('id', -1)]),
                    ('parent', '=', None)
                ]],
        help="Where outgoing stock is sent from.")
    storage_location = fields.Many2One(
        "stock.location",
        "Storage",
        states={
            'invisible': Eval('type') != 'warehouse',
            'required': Eval('type') == 'warehouse',
        },
        domain=[('type', 'in', ['storage', 'view']),
                [
                    'OR', ('parent', 'child_of', [Eval('id', -1)]),
                    ('parent', '=', None)
                ]],
        help="The top level location where stock is stored.")
    picking_location = fields.Many2One(
        'stock.location',
        'Picking',
        states={
            'invisible': Eval('type') != 'warehouse',
        },
        domain=[
            ('type', '=', 'storage'),
            ('parent', 'child_of', [Eval('storage_location', -1)]),
        ],
        help="Where stock is picked from.\n"
        "Leave empty to use the storage location.")
    lost_found_location = fields.Many2One(
        'stock.location',
        "Lost and Found",
        states={
            'invisible': Eval('type') != 'warehouse',
            'readonly': ~Eval('active'),
        },
        domain=[
            ('type', '=', 'lost_found'),
        ],
        help="Used, by inventories, when correcting stock levels "
        "in the warehouse.")
    waste_locations = fields.Many2Many(
        'stock.location.waste',
        'warehouse',
        'location',
        "Waste Locations",
        states={
            'invisible': Eval('type') != 'warehouse',
        },
        domain=[
            ('type', '=', 'lost_found'),
        ],
        help="The locations used for waste products from the warehouse.")
    waste_warehouses = fields.Many2Many(
        'stock.location.waste',
        'location',
        'warehouse',
        "Waste Warehouses",
        states={
            'invisible': Eval('type') != 'lost_found',
        },
        domain=[
            ('type', '=', 'warehouse'),
        ],
        help="The warehouses that use the location for waste products.")

    quantity = fields.Function(fields.Float(
        "Quantity",
        digits=(16, Eval('quantity_uom_digits', 2)),
        help="The amount of stock in the location."),
                               'get_quantity',
                               searcher='search_quantity')
    forecast_quantity = fields.Function(fields.Float(
        "Forecast Quantity",
        digits=(16, Eval('quantity_uom_digits', 2)),
        help="The amount of stock expected to be in the location."),
                                        'get_quantity',
                                        searcher='search_quantity')
    quantity_uom = fields.Function(
        fields.Many2One('product.uom', "Quantity UOM"), 'get_quantity_uom')
    quantity_uom_digits = fields.Function(
        fields.Integer("Quantity UOM Digits"), 'get_quantity_uom')
    cost_value = fields.Function(
        fields.Numeric("Cost Value",
                       digits=price_digits,
                       help="The value of the stock in the location."),
        'get_cost_value')

    @classmethod
    def __setup__(cls):
        super(Location, cls).__setup__()
        cls._order.insert(0, ('name', 'ASC'))

        parent_domain = [[
            'OR',
            ('parent.flat_childs', '=', False),
            ('parent', '=', None),
        ]]
        childs_domain = [
            If(Eval('flat_childs', False), ('childs', '=', None), ()),
        ]
        childs_mapping = cls._childs_domain()
        for type_, allowed_parents in cls._parent_domain().items():
            parent_domain.append(
                If(Eval('type') == type_, ('type', 'in', allowed_parents), ()))
            childs_domain.append(
                If(
                    Eval('type') == type_,
                    ('type', 'in', childs_mapping[type_]), ()))
        cls.parent.domain = parent_domain
        cls.childs.domain = childs_domain

    @classmethod
    def _parent_domain(cls):
        '''Returns a dict with location types as keys and a list of allowed
        parent location types as values'''
        return {
            'customer': ['customer'],
            'supplier': ['supplier'],
            'production': ['production'],
            'lost_found': ['lost_found'],
            'view': ['warehouse', 'view', 'storage'],
            'storage': ['warehouse', 'view', 'storage'],
            'warehouse': ['view'],
        }

    @classmethod
    def _childs_domain(cls):
        childs_domain = {}
        for type_, allowed_parents in cls._parent_domain().items():
            for parent in allowed_parents:
                childs_domain.setdefault(parent, [])
                childs_domain[parent].append(type_)
        return childs_domain

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

        table = cls.__table_handler__(module_name)
        table.index_action(['left', 'right'], 'add')

    @classmethod
    def validate_fields(cls, locations, field_names):
        super().validate_fields(locations, field_names)
        inactives = []
        for location in locations:
            location.check_type_for_moves(field_names)
            if 'active' in field_names and not location.active:
                inactives.append(location)
        cls.check_inactive(inactives)

    def check_type_for_moves(self, field_names=None):
        """ Check locations with moves have types compatible with moves. """
        pool = Pool()
        Move = pool.get('stock.move')
        if field_names and 'type' not in field_names:
            return
        invalid_move_types = ['warehouse', 'view']
        if self.type in invalid_move_types:
            # Use root to compute for all companies
            with Transaction().set_user(0):
                moves = Move.search([
                    [
                        'OR',
                        ('to_location', '=', self.id),
                        ('from_location', '=', self.id),
                    ],
                    ('state', 'not in', ['staging', 'draft']),
                ],
                                    order=[],
                                    limit=1)
            if moves:
                raise LocationValidationError(
                    gettext('stock.msg_location_invalid_type_for_moves',
                            location=self.rec_name,
                            type=self.type_string))

    @classmethod
    def check_inactive(cls, locations):
        "Check inactive location are empty"
        assert all(not l.active for l in locations)
        empty = cls.get_empty_locations(locations)
        non_empty = set(locations) - set(empty)
        if non_empty:
            raise LocationValidationError(
                gettext('stock.msg_location_inactive_not_empty',
                        location=next(iter(non_empty)).rec_name))

    @classmethod
    def get_empty_locations(cls, locations=None):
        pool = Pool()
        Move = pool.get('stock.move')
        if locations is None:
            locations = cls.search([])
        if not locations:
            return []
        location_ids = list(map(int, locations))
        # Use root to compute for all companies
        # and ensures inactive locations are in the query
        with Transaction().set_user(0), \
                Transaction().set_context(active_test=False):
            query = Move.compute_quantities_query(location_ids,
                                                  with_childs=True)
            quantities = Move.compute_quantities(query,
                                                 location_ids,
                                                 with_childs=True)
            empty = set(location_ids)
            for (location_id, product), quantity in quantities.items():
                if quantity:
                    empty.discard(location_id)
            for sub_ids in grouped_slice(list(empty)):
                sub_ids = list(sub_ids)
                moves = Move.search([
                    ('state', 'not in', ['done', 'cancelled']),
                    [
                        'OR',
                        ('from_location', 'in', sub_ids),
                        ('to_location', 'in', sub_ids),
                    ],
                ])
                for move in moves:
                    for location in [move.from_location, move.to_location]:
                        empty.discard(location.id)
        return cls.browse(empty)

    @staticmethod
    def default_left():
        return 0

    @staticmethod
    def default_right():
        return 0

    @classmethod
    def default_flat_childs(cls):
        return False

    @staticmethod
    def default_type():
        return 'storage'

    @classmethod
    def check_xml_record(cls, records, values):
        return True

    def get_warehouse(self, name):
        # Order by descending left to get the first one in the tree
        with Transaction().set_context(active_test=False):
            locations = self.search([
                ('parent', 'parent_of', [self.id]),
                ('type', '=', 'warehouse'),
            ],
                                    order=[('left', 'DESC')])
        if locations:
            return locations[0].id

    @classmethod
    def get_default_warehouse(cls):
        warehouse = Transaction().context.get('warehouse')
        if warehouse:
            return warehouse

        warehouse = cls._default_warehouse_cache.get(None, -1)
        if warehouse == -1:
            warehouses = cls.search([
                ('type', '=', 'warehouse'),
            ], limit=2)
            if len(warehouses) == 1:
                warehouse = warehouses[0].id
            else:
                warehouse = None
            cls._default_warehouse_cache.set(None, warehouse)
        return warehouse

    @property
    def lost_found_used(self):
        if self.warehouse:
            return self.warehouse.lost_found_location

    @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,
            (cls._rec_name, ) + tuple(clause[1:]),
            ('code', ) + tuple(clause[1:]),
        ]

    @classmethod
    def _get_quantity_grouping(cls):
        context = Transaction().context
        grouping, grouping_filter, key = (), (), []
        if context.get('product') is not None:
            grouping = ('product', )
            grouping_filter = ([context['product']], )
            key = (context['product'], )
        elif context.get('product_template') is not None:
            grouping = ('product.template', )
            grouping_filter = ([context['product_template']], )
            key = (context['product_template'], )
        return grouping, grouping_filter, key

    @classmethod
    def get_quantity(cls, locations, name):
        pool = Pool()
        Product = pool.get('product.product')
        Date_ = pool.get('ir.date')
        trans_context = Transaction().context

        def valid_context(name):
            return (trans_context.get(name) is not None
                    and isinstance(trans_context[name], int))

        context = {}
        if (name == 'quantity' and (trans_context.get(
                'stock_date_end', datetime.date.max) > Date_.today())):
            context['stock_date_end'] = Date_.today()

        if name == 'forecast_quantity':
            context['forecast'] = True
            if not trans_context.get('stock_date_end'):
                context['stock_date_end'] = datetime.date.max

        grouping, grouping_filter, key = cls._get_quantity_grouping()
        if not grouping:
            return {loc.id: None for loc in locations}

        pbl = {}
        for sub_locations in grouped_slice(locations):
            location_ids = [l.id for l in sub_locations]
            with Transaction().set_context(context):
                pbl.update(
                    Product.products_by_location(
                        location_ids,
                        grouping=grouping,
                        grouping_filter=grouping_filter,
                        with_childs=trans_context.get('with_childs', True)))

        return dict(
            (loc.id, pbl.get((loc.id, ) + key, 0)) for loc in locations)

    @classmethod
    def search_quantity(cls, name, domain):
        _, operator_, operand = domain
        operator_ = {
            '=': operator.eq,
            '>=': operator.ge,
            '>': operator.gt,
            '<=': operator.le,
            '<': operator.lt,
            '!=': operator.ne,
            'in': lambda v, l: v in l,
            'not in': lambda v, l: v not in l,
        }.get(operator_, lambda v, l: False)

        ids = []
        for location in cls.search([]):
            if operator_(getattr(location, name), operand):
                ids.append(location.id)
        return [('id', 'in', ids)]

    @classmethod
    def get_quantity_uom(cls, locations, name):
        pool = Pool()
        Product = pool.get('product.product')
        Template = pool.get('product.template')
        context = Transaction().context
        value = None
        uom = None
        if context.get('product') is not None:
            product = Product(context['product'])
            uom = product.default_uom
        elif context.get('product_template') is not None:
            template = Template(context['product_template'])
            uom = template.default_uom
        if uom:
            if name == 'quantity_uom':
                value = uom.id
            elif name == 'quantity_uom_digits':
                value = uom.digits
        return {l.id: value for l in locations}

    @classmethod
    def get_cost_value(cls, locations, name):
        pool = Pool()
        Product = pool.get('product.product')
        Template = pool.get('product.template')
        trans_context = Transaction().context
        cost_values = {l.id: None for l in locations}

        def valid_context(name):
            return (trans_context.get(name) is not None
                    and isinstance(trans_context[name], int))

        if not any(map(valid_context, ['product', 'product_template'])):
            return cost_values

        def get_record():
            if trans_context.get('product') is not None:
                return Product(trans_context['product'])
            else:
                return Template(trans_context['product_template'])

        context = {}
        if 'stock_date_end' in trans_context:
            # Use the last cost_price of the day
            context['_datetime'] = datetime.datetime.combine(
                trans_context['stock_date_end'], datetime.time.max)
            # The date could be before the product creation
            record = get_record()
            if record.create_date > context['_datetime']:
                return cost_values
        with Transaction().set_context(context):
            cost_price = get_record().cost_price
        # The template may have more than one product
        if cost_price is not None:
            for location in locations:
                cost_values[location.id] = round_price(
                    Decimal(str(location.quantity)) * cost_price)
        return cost_values

    @classmethod
    def _set_warehouse_parent(cls, locations):
        '''
        Set the parent of child location of warehouse if not set
        '''
        to_update = set()
        to_save = []
        for location in locations:
            if location.type == 'warehouse':
                if not location.input_location.parent:
                    to_update.add(location.input_location)
                if not location.output_location.parent:
                    to_update.add(location.output_location)
                if not location.storage_location.parent:
                    to_update.add(location.storage_location)
                if to_update:
                    for child_location in to_update:
                        child_location.parent = location
                        to_save.append(child_location)
                    to_update.clear()
        cls.save(to_save)

    @classmethod
    def create(cls, vlist):
        locations = super(Location, cls).create(vlist)
        cls._set_warehouse_parent(locations)
        cls._default_warehouse_cache.clear()
        return locations

    @classmethod
    def write(cls, *args):
        super(Location, cls).write(*args)
        locations = sum(args[::2], [])
        cls._set_warehouse_parent(locations)
        cls._default_warehouse_cache.clear()

        ids = [l.id for l in locations]
        warehouses = cls.search([('type', '=', 'warehouse'),
                                 [
                                     'OR',
                                     ('storage_location', 'in', ids),
                                     ('input_location', 'in', ids),
                                     ('output_location', 'in', ids),
                                 ]])

        fields = ('storage_location', 'input_location', 'output_location')
        wh2childs = {}
        for warehouse in warehouses:
            in_out_sto = (getattr(warehouse, f).id for f in fields)
            for location in locations:
                if location.id not in in_out_sto:
                    continue
                childs = wh2childs.setdefault(
                    warehouse.id,
                    cls.search([
                        ('parent', 'child_of', warehouse.id),
                    ]))
                if location not in childs:
                    raise LocationValidationError(
                        gettext('stock.msg_location_child_of_warehouse',
                                location=location.rec_name,
                                warehouse=warehouse.rec_name))

    @classmethod
    def delete(cls, *args):
        super().delete(*args)
        cls._default_warehouse_cache.clear()

    @classmethod
    def copy(cls, locations, default=None):
        if default is None:
            default = {}
        else:
            default = default.copy()

        res = []
        for location in locations:
            if location.type == 'warehouse':

                wh_default = default.copy()
                wh_default['type'] = 'view'
                wh_default['input_location'] = None
                wh_default['output_location'] = None
                wh_default['storage_location'] = None
                wh_default['childs'] = None

                new_location, = super(Location, cls).copy([location],
                                                          default=wh_default)

                with Transaction().set_context(
                        cp_warehouse_locations={
                            'input_location': location.input_location.id,
                            'output_location': location.output_location.id,
                            'storage_location': location.storage_location.id,
                        },
                        cp_warehouse_id=new_location.id):
                    cls.copy(location.childs,
                             default={'parent': new_location.id})
                cls.write([new_location], {
                    'type': 'warehouse',
                })
            else:
                new_location, = super(Location, cls).copy([location],
                                                          default=default)
                warehouse_locations = Transaction().context.get(
                    'cp_warehouse_locations') or {}
                if location.id in warehouse_locations.values():
                    cp_warehouse = cls(
                        Transaction().context['cp_warehouse_id'])
                    for field, loc_id in warehouse_locations.items():
                        if loc_id == location.id:
                            cls.write([cp_warehouse], {
                                field: new_location.id,
                            })

            res.append(new_location)
        return res

    @classmethod
    def view_attributes(cls):
        storage_types = Eval('type').in_(['storage', 'warehouse', 'view'])
        return super().view_attributes() + [
            ('/tree/field[@name="quantity"]', 'visual',
             If(storage_types &
                (Eval('quantity', 0) < 0), 'danger', ''), ['type']),
            ('/tree/field[@name="forecast_quantity"]', 'visual',
             If(storage_types &
                (Eval('forecast_quantity', 0) < 0), 'warning', ''), ['type']),
        ]
Esempio n. 3
0
class Party(DeactivableMixin, ModelSQL, ModelView, MultiValueMixin):
    "Party"
    __name__ = 'party.party'

    name = fields.Char(
        "Name", select=True,
        help="The main identifier of the party.")
    code = fields.Char('Code', required=True, select=True,
        states={
            'readonly': Eval('code_readonly', True),
            },
        depends=['code_readonly'],
        help="The unique identifier of the party.")
    code_readonly = fields.Function(fields.Boolean('Code Readonly'),
        'get_code_readonly')
    lang = fields.MultiValue(
        fields.Many2One('ir.lang', "Language",
            help="Used to translate communications with the party."))
    langs = fields.One2Many(
        'party.party.lang', 'party', "Languages")
    identifiers = fields.One2Many(
        'party.identifier', 'party', "Identifiers",
        help="Add other identifiers of the party.")
    tax_identifier = fields.Function(fields.Many2One(
            'party.identifier', 'Tax Identifier',
            help="The identifier used for tax report."),
        'get_tax_identifier', searcher='search_tax_identifier')
    addresses = fields.One2Many('party.address', 'party', "Addresses")
    contact_mechanisms = fields.One2Many(
        'party.contact_mechanism', 'party', "Contact Mechanisms")
    categories = fields.Many2Many(
        'party.party-party.category', 'party', 'category', "Categories",
        help="The categories the party belongs to.")
    replaced_by = fields.Many2One('party.party', "Replaced By", readonly=True,
        states={
            'invisible': ~Eval('replaced_by'),
            },
        help="The party replacing this one.")
    full_name = fields.Function(fields.Char('Full Name'), 'get_full_name')
    phone = fields.Function(fields.Char('Phone'), 'get_mechanism')
    mobile = fields.Function(fields.Char('Mobile'), 'get_mechanism')
    fax = fields.Function(fields.Char('Fax'), 'get_mechanism')
    email = fields.Function(fields.Char('E-Mail'), 'get_mechanism')
    website = fields.Function(fields.Char('Website'), 'get_mechanism')
    distance = fields.Function(fields.Integer('Distance'), 'get_distance')

    @classmethod
    def __setup__(cls):
        super(Party, cls).__setup__()
        t = cls.__table__()
        cls._sql_constraints = [
            ('code_uniq', Unique(t, t.code), 'party.msg_party_code_unique')
            ]
        cls._order.insert(0, ('distance', 'ASC NULLS LAST'))
        cls._order.insert(1, ('name', 'ASC'))
        cls.active.states.update({
                'readonly': Bool(Eval('replaced_by')),
                })
        cls.active.depends.append('replaced_by')

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

        table_h = cls.__table_handler__(module_name)

        # Migration from 3.8
        table_h.not_null_action('name', 'remove')

    @staticmethod
    def order_code(tables):
        table, _ = tables[None]
        return [CharLength(table.code), table.code]

    @staticmethod
    def default_categories():
        return Transaction().context.get('categories', [])

    @staticmethod
    def default_addresses():
        if Transaction().user == 0:
            return []
        return [{}]

    @classmethod
    def default_lang(cls, **pattern):
        Configuration = Pool().get('party.configuration')
        config = Configuration(1)
        lang = config.get_multivalue('party_lang', **pattern)
        return lang.id if lang else None

    @classmethod
    def default_code_readonly(cls, **pattern):
        Configuration = Pool().get('party.configuration')
        config = Configuration(1)
        return bool(config.get_multivalue('party_sequence', **pattern))

    def get_code_readonly(self, name):
        return True

    @classmethod
    def tax_identifier_types(cls):
        return [
            'ad_nrt', 'al_nipt', 'ar_cuit', 'be_vat', 'bg_vat', 'by_unp',
            'ch_vat', 'cl_rut', 'cn_uscc', 'co_rut', 'cr_cpj', 'cz_dic',
            'de_vat', 'dk_cvr', 'do_rnc', 'ec_ruc', 'ee_kmkr', 'es_nif',
            'eu_vat', 'fi_alv', 'fr_tva', 'gb_vat', 'gr_vat', 'gt_nit',
            'hu_anum', 'id_npwp', 'ie_vat', 'il_hp', 'is_vsk', 'it_iva',
            'jp_cn', 'kr_brn', 'lt_pvm', 'lu_tva', 'lv_pvn', 'mc_tva',
            'md_idno', 'mt_vat', 'mx_rfc', 'nl_btw', 'no_mva', 'nz_ird',
            'pe_ruc', 'pl_nip', 'pt_nif', 'py_ruc', 'ro_cf', 'rs_pib',
            'ru_inn', 'se_vat', 'si_ddv', 'sk_dph', 'sm_coe', 'us_atin',
            'us_ein', 'us_itin', 'us_ptin', 'us_ssn', 'us_tin', 'uy_ruc',
            've_rif', 'za_tin']

    def get_tax_identifier(self, name):
        types = self.tax_identifier_types()
        for identifier in self.identifiers:
            if identifier.type in types:
                return identifier.id

    @classmethod
    def search_tax_identifier(cls, name, clause):
        _, operator, value = clause
        types = cls.tax_identifier_types()
        domain = [
            ('identifiers', 'where', [
                    ('code', operator, value),
                    ('type', 'in', types),
                    ]),
            ]
        # Add party without tax identifier
        if ((operator == '=' and value is None)
                or (operator == 'in' and None in value)):
            domain = ['OR',
                domain, [
                    ('identifiers', 'not where', [
                            ('type', 'in', types),
                            ]),
                    ],
                ]
        return domain

    def get_full_name(self, name):
        return self.name

    def get_mechanism(self, name):
        for mechanism in self.contact_mechanisms:
            if mechanism.type == name:
                return mechanism.value
        return ''

    @classmethod
    def _distance_query(cls, usages=None, party=None, depth=None):
        context = Transaction().context
        if party is None:
            party = context.get('related_party')

        if not party:
            return

        table = cls.__table__()
        return table.select(
            table.id.as_('to'),
            Literal(0).as_('distance'),
            where=(table.id == party))

    @classmethod
    def get_distance(cls, parties, name):
        distances = {p.id: None for p in parties}
        query = cls._distance_query()
        if query:
            cursor = Transaction().connection.cursor()
            cursor.execute(*query.select(
                    query.to.as_('to'),
                    Min(query.distance).as_('distance'),
                    group_by=[query.to]))
            distances.update(cursor)
        return distances

    @classmethod
    def order_distance(cls, tables):
        party, _ = tables[None]
        key = 'distance'
        if key not in tables:
            query = cls._distance_query()
            if not query:
                return []
            query = query.select(
                    query.to.as_('to'),
                    Min(query.distance).as_('distance'),
                    group_by=[query.to])
            join = party.join(query, type_='LEFT',
                condition=query.to == party.id)
            tables[key] = {
                None: (join.right, join.condition),
                }
        else:
            query, _ = tables[key][None]
        return [query.distance]

    @classmethod
    def _new_code(cls, **pattern):
        pool = Pool()
        Configuration = pool.get('party.configuration')
        config = Configuration(1)
        sequence = config.get_multivalue('party_sequence', **pattern)
        if sequence:
            return sequence.get()

    @classmethod
    def create(cls, vlist):
        vlist = [x.copy() for x in vlist]
        for values in vlist:
            if not values.get('code'):
                values['code'] = cls._new_code()
            values.setdefault('addresses', None)
        return super(Party, cls).create(vlist)

    @classmethod
    def copy(cls, parties, default=None):
        if default is None:
            default = {}
        else:
            default = default.copy()
        default.setdefault('code', None)
        return super(Party, cls).copy(parties, default=default)

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

    def get_rec_name(self, name):
        if not self.name:
            return '[' + self.code + ']'
        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'
        code_value = clause[2]
        if clause[1].endswith('like'):
            code_value = lstrip_wildcard(clause[2])
        return [bool_op,
            ('code', clause[1], code_value) + tuple(clause[3:]),
            ('identifiers.code', clause[1], code_value) + tuple(clause[3:]),
            ('name',) + tuple(clause[1:]),
            ('contact_mechanisms.rec_name',) + tuple(clause[1:]),
            ]

    def address_get(self, type=None):
        """
        Try to find an address for the given type, if no type matches
        the first address is returned.
        """
        default_address = None
        if self.addresses:
            default_address = self.addresses[0]
            if type:
                for address in self.addresses:
                    if getattr(address, type):
                        return address
        return default_address

    def contact_mechanism_get(self, types=None, usage=None):
        """
        Try to find a contact mechanism for the given types and usage, if no
        usage matches the first mechanism of the given types is returned.
        """
        default_mechanism = None
        if types:
            if isinstance(types, str):
                types = {types}
            mechanisms = [m for m in self.contact_mechanisms
                if m.type in types]
        else:
            mechanisms = self.contact_mechanisms
        if mechanisms:
            default_mechanism = mechanisms[0]
            if usage:
                for mechanism in mechanisms:
                    if getattr(mechanism, usage):
                        return mechanism
        return default_mechanism
Esempio n. 4
0
class ShipmentOut:
    "Shipment Out"
    __name__ = 'stock.shipment.out'

    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')

    tracking_number = fields.Char('Tracking Number',
                                  states=STATES,
                                  depends=['state'])

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

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

    @classmethod
    def __setup__(cls):
        super(ShipmentOut, cls).__setup__()
        cls._buttons.update({
            'label_wizard': {
                'invisible':
                Or((~Eval('state').in_(['packed', 'done'])),
                   (Eval('tracking_number') != '')),
                'icon':
                'tryton-executable',
            },
        })
        cls._error_messages.update({
            'no_shipments':
            'There must be atleast one shipment.',
            'too_many_shipments':
            'The wizard can be called on only one shipment',
            'tracking_number_already_present':
            'Tracking Number is already present for this shipment.',
            'invalid_state':
            'Labels can only be generated when the '
            'shipment is in Packed or Done states only',
            'wrong_carrier':
            'Carrier for selected shipment is not of %s',
            'no_packages':
            'Shipment %s has no packages',
            'warehouse_address_missing':
            'Warehouse address is missing',
        })

    @classmethod
    def copy(cls, shipments, default=None):
        if default is None:
            default = {}
        default = default.copy()
        default['tracking_number'] = None
        return super(ShipmentOut, cls).copy(shipments, default=default)

    @classmethod
    @ModelView.button_action('shipping.wizard_generate_shipping_label')
    def label_wizard(cls, shipments):
        if len(shipments) == 0:
            cls.raise_user_error('no_shipments')
        elif len(shipments) > 1:
            cls.raise_user_error('too_many_shipments')

    @fields.depends('delivery_address', 'warehouse')
    def on_change_with_is_international_shipping(self, name=None):
        """
        Return True if international shipping
        """
        from_address = self._get_ship_from_address()
        if self.delivery_address and from_address and \
           from_address.country and self.delivery_address.country and \
           from_address.country != self.delivery_address.country:
            return True
        return False

    def get_weight_uom(self, name):
        """
        Returns weight uom for the package
        """
        return self._get_weight_uom().id

    def _get_weight_uom(self):
        """
        Returns Pound as default value for uom

        Downstream module can override this method to change weight uom as per
        carrier
        """
        UOM = Pool().get('product.uom')

        return UOM.search([('symbol', '=', 'lb')])[0]

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

    def allow_label_generation(self):
        """
        Shipment must be in the right states and tracking number must not
        be present.
        """
        if self.state not in ('packed', 'done'):
            self.raise_user_error('invalid_state')

        if self.tracking_number:
            self.raise_user_error('tracking_number_already_present')

        return True
Esempio n. 5
0
class OverTimeAllowance(Workflow, ModelSQL, ModelView):
    """OverTime Allowance for an Employee"""

    __name__ = 'hr.allowance.ota'

    _STATES = {'readonly': ~Eval('state').in_(['draft'])}
    _DEPENDS = ['state']
    salary_code = fields.Char('Salary Code',
                              states=_STATES,
                              required=True,
                              depends=_DEPENDS)
    employee = fields.Many2One('company.employee',
                               'Employee Name',
                               states=_STATES,
                               required=True,
                               depends=_DEPENDS)
    designation = fields.Many2One('employee.designation',
                                  'Designation',
                                  states=_STATES,
                                  required=True,
                                  depends=_DEPENDS)
    department = fields.Many2One('company.department',
                                 'Department',
                                 states=_STATES,
                                 required=True,
                                 depends=_DEPENDS)

    from_date = fields.Date('From Date', states=_STATES, depends=_DEPENDS)
    to_date = fields.Date('To Date', states=_STATES, depends=_DEPENDS)
    from_date = fields.Date('From Date', states=_STATES, depends=_DEPENDS)
    to_date = fields.Date('To Date', states=_STATES, depends=_DEPENDS)
    amount = fields.Integer('Amount',
                            states=_STATES,
                            required=True,
                            depends=_DEPENDS)
    state = fields.Selection([
        ('draft', 'Draft'),
        ('submit', 'Submit'),
        ('cash_section_officer', 'Cash Section_officer'),
        ('cancel', 'Cancel'),
        ('approve', 'Approve'),
    ],
                             'Status',
                             readonly=True)

    @fields.depends('employee')
    def on_change_employee(self):
        if self.employee:
            self.salary_code = self.employee.salary_code if self.employee.salary_code else None
            self.designation = self.employee.designation if self.employee.designation else None
            self.department = self.employee.department if self.employee.department else None

    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def default_from_date():
        return datetime.date.today()

    @staticmethod
    def default_employee():
        global current_employee
        current_employee = None
        pool = Pool()
        Employee = pool.get('company.employee')
        employee_id = Transaction().context.get('employee')
        employee = Employee.search([('id', '=', employee_id)])
        if employee != []:
            current_employee = employee[0]
        return current_employee.id if current_employee else None

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._buttons.update({
            'submit': {
                'invisible': ~Eval('state').in_(['draft'])
            },
            'send_for_cash_section_officer_approval': {
                'invisible': ~Eval('state').in_(['submit'])
            },
            'approve': {
                'invisible': ~Eval('state').in_(['cash_section_officer'])
            },
            'cancel': {
                'invisible': Eval('state').in_(['draft', 'approve', 'cancel'])
            },
        })
        cls._transitions |= set((
            ('draft', 'submit'),
            ('submit', 'cash_section_officer'),
            ('submit', 'cancel'),
            ('cash_section_officer', 'approve'),
            ('cash_section_officer', 'cancel'),
        ))

    @classmethod
    @Workflow.transition('submit')
    def submit(cls, records):
        pass

    @classmethod
    @Workflow.transition('cash_section_officer')
    def send_for_cash_section_officer_approval(cls, records):
        pass

    @classmethod
    @Workflow.transition('approve')
    def approve(cls, records):
        pass

    @classmethod
    @Workflow.transition('cancel')
    def cancel(cls, records):
        pass
Esempio n. 6
0
class RenumerationBill(Workflow, ModelSQL, ModelView):
    '''Examination Renumeration Bill'''

    __name__ = 'exam_section.renumeration_bill'

    state = fields.Selection([('draft', 'Draft'), ('confirm', 'Confirm'),
                              ('aao_approval', 'AAO Approval'),
                              ('ao_approval', 'AO Approval'),
                              ('ace_approval', 'ACE Approval'),
                              ('adean_approval', 'Assistant Dean Approval'),
                              ('dean_approval', 'Dean Approval'),
                              ('ao_approval_2', 'AO Approval 2'),
                              ('approved', 'Approved'),
                              ('rejected', 'Rejected')],
                             'Status',
                             readonly=True)
    type_of_examiner = fields.Selection(
        [('internal', 'Internal'), ('external', 'External')],
        'Type of Examiner',
        states={'readonly': ~Eval('state').in_(['draft'])},
        depends=['state'])
    employee = fields.Many2One(
        'company.employee',
        'Employee',
        states={
            'invisible': Eval('type_of_examiner') == 'external',
            'readonly': ~Eval('state').in_(['draft'])
        },
        depends=['type_of_examiner', 'state'],
    )

    examiner_name = fields.Char('Name of Examiner',
                                states={
                                    'invisible':
                                    Eval('type_of_examiner') == 'internal',
                                    'readonly': ~Eval('state').in_(['draft'])
                                },
                                depends=['type_of_examiner', 'state'])

    designation = fields.Char(
        'Designation',
        states={'readonly': ~Eval('state').in_(['draft'])},
        depends=['state'])

    address = fields.Text('Address',
                          states={
                              'invisible':
                              Eval('type_of_examiner') == 'internal',
                              'readonly': ~Eval('state').in_(['draft'])
                          },
                          depends=['type_of_examiner', 'state'])

    pincode = fields.Integer('Pin Code',
                             states={
                                 'invisible':
                                 Eval('type_of_examiner') == 'internal',
                                 'readonly': ~Eval('state').in_(['draft'])
                             },
                             depends=['type_of_examiner', 'state'])

    exam = fields.Many2One('exam_section.exam',
                           'Exam',
                           states={
                               'readonly': ~Eval('state').in_(['draft']),
                           },
                           depends=['state'])
    course_name = fields.Char('Name of Course',
                              states={
                                  'readonly': ~Eval('state').in_(['draft']),
                              },
                              depends=['state'])
    purpose = fields.One2Many('exam_type.purpose_and_pay_renum',
                              'renumeration',
                              'Purpose',
                              states={
                                  'readonly': ~Eval('state').in_(['draft']),
                              },
                              depends=['state'])
    total_amount = fields.Function(fields.Float('Total Amount'),
                                   'get_total_amount')
    net_amount = fields.Function(
        fields.Float(
            'Net Amount',
            help='Less student benevolent fund(5%) deducted for AIIMS employee'
        ), 'get_net_amount')
    bank_name = fields.Char('Bank Name',
                            states={
                                'invisible':
                                Eval('type_of_examiner') == 'internal',
                                'readonly': ~Eval('state').in_(['draft'])
                            },
                            depends=['type_of_examiner', 'state'])
    bank_address = fields.Char('Bank Address',
                               states={
                                   'invisible':
                                   Eval('type_of_examiner') == 'internal',
                                   'readonly': ~Eval('state').in_(['draft'])
                               },
                               depends=['type_of_examiner', 'state'])
    account_number = fields.Char('Account Number',
                                 states={
                                     'invisible':
                                     Eval('type_of_examiner') == 'internal',
                                     'readonly': ~Eval('state').in_(['draft'])
                                 },
                                 depends=['type_of_examiner', 'state'])
    ifsc_code = fields.Char('IFSC Code',
                            states={
                                'invisible':
                                Eval('type_of_examiner') == 'internal',
                                'readonly': ~Eval('state').in_(['draft'])
                            },
                            depends=['type_of_examiner', 'state'])

    @staticmethod
    def default_state():
        return 'draft'

    @fields.depends('employee')
    def on_change_with_designation(self):
        '''Fill up employee designation field 
           as soon as employee field is changed'''
        if self.employee:
            if self.employee.designation:
                return self.employee.designation.name

    def get_total_amount(self, name):
        '''Calculate total amount of Renumeration Bill'''
        res = 0
        if self.purpose:
            for purpose in self.purpose:
                res += purpose.amount
        return res

    def get_net_amount(self, name):
        '''Calculate net amount of Renumeration Bill
           (5% is deducted for AIIMS Employee which goes
           to less student benevolent fund)'''
        res = 0
        total_amount = self.total_amount
        if self.type_of_examiner == 'internal':
            res = 0.95 * (total_amount)
        else:
            res = total_amount
        return res

    @classmethod
    def __setup__(cls):
        '''Setup error messages, workflow transitions, and button properties
           when an instance of this class is initialized'''
        super().__setup__()
        cls._error_messages.update(
            {'submitted_bill': 'A Submitted Bill can not be deleted'})
        cls._transitions = set(
            (('draft', 'confirm'), ('confirm', 'aao_approval'),
             ('aao_approval', 'ao_approval'), ('ao_approval', 'ace_approval'),
             ('ace_approval', 'adean_approval'), ('adean_approval',
                                                  'dean_approval'),
             ('dean_approval', 'ao_approval_2'), ('ao_approval_2',
                                                  'approved')))
        cls._buttons.update({
            'confirm_data': {
                'invisible': ~Eval('state').in_(['draft'])
            },
            'send_for_aao_approval': {
                'invisible': ~Eval('state').in_(['confirm'])
            },
            'send_for_ao_approval': {
                'invisible': ~Eval('state').in_(['aao_approval'])
            },
            'send_for_ace_approval': {
                'invisible': ~Eval('state').in_(['ao_approval'])
            },
            'send_for_adean_approval': {
                'invisible': ~Eval('state').in_(['ace_approval'])
            },
            'send_for_dean_approval': {
                'invisible': ~Eval('state').in_(['adean_approval'])
            },
            'send_for_ao_approval_2': {
                'invisible': ~Eval('state').in_(['dean_approval'])
            },
        })

    '''Button functions for executing workflow transitions
       (except delete)'''

    @classmethod
    @Workflow.transition('confirm')
    def confirm_data(cls, records):
        '''Change status of bill to confirm'''
        pass

    @classmethod
    @Workflow.transition('aao_approval')
    def send_for_aao_approval(cls, records):
        '''Change status of bill to aao_approval'''
        pass

    @classmethod
    @Workflow.transition('ao_approval')
    def send_for_ao_approval(cls, records):
        '''Change status of bill to ao_approval'''
        pass

    @classmethod
    @Workflow.transition('ace_approval')
    def send_for_ace_approval(cls, records):
        '''Change status of bill to ace_approval'''
        pass

    @classmethod
    @Workflow.transition('adean_approval')
    def send_for_adean_approval(cls, records):
        '''Change status of bill to adean_approval'''
        pass

    @classmethod
    @Workflow.transition('dean_approval')
    def send_for_dean_approval(cls, records):
        '''Change status of bill to dean_approval'''
        pass

    @classmethod
    @Workflow.transition('ao_approval_2')
    def send_for_ao_approval_2(cls, records):
        '''Change status of bill to ao_approval_2'''
        pass

    @classmethod
    def delete(cls, records):
        '''Override delete to ensure that the submitted bills are not deleted.
        '''
        res = super().delete(records)
        for record in records:
            if record.state not in ('draft'):
                cls.raise_user_error('submitted_bill')
        return res
Esempio n. 7
0
class TADABill(Workflow, ModelSQL, ModelView):
    '''Examination TA/DA Bill'''

    __name__ = 'exam_section.ta_da_bill'

    employee = fields.Many2One(
        'company.employee',
        'Employee',
        states={'readonly': ~Eval('state').in_(['draft'])},
        depends=['state'])
    state = fields.Selection([('draft', 'Draft'), ('confirm', 'Confirm'),
                              ('aao_approval', 'AAO Approval'),
                              ('ao_approval', 'AO Approval'),
                              ('ace_approval', 'ACE Approval'),
                              ('adean_approval', 'Assistant Dean Approval'),
                              ('dean_approval', 'Dean Approval'),
                              ('ao_approval_2', 'AO Approval 2'),
                              ('approved', 'Approved')],
                             'Status',
                             readonly=True)
    submit_date = fields.Date(
        'Submit Date',
        states={'readonly': ~Eval('state').in_([
            'draft',
            'confirm',
        ])})
    approved_date = fields.Date(
        'Approved Date', states={'readonly': Eval('state').in_(['approved'])})
    center = fields.Many2One('exam.centers', 'Center')
    designation = fields.Many2One(
        'employee.designation',
        'Designation',
        states={'readonly': ~Eval('state').in_(['draft'])},
        depends=['state'])
    department = fields.Many2One(
        'company.department',
        'Department',
        states={'readonly': ~Eval('state').in_(['draft'])},
        depends=['state'])
    purpose = fields.Char('Purpose of travel',
                          states={'readonly': ~Eval('state').in_(['draft'])},
                          depends=['state'],
                          required=True)
    journey = fields.One2Many(
        'exam_section.ta_da.journey',
        'ta_da',
        'Journey',
        states={'readonly': ~Eval('state').in_(['draft'])},
        depends=['state'])
    hotel_food = fields.One2Many(
        'exam_section.ta_da.hotel_food',
        'ta_da',
        'Hotel/Food',
        states={'readonly': ~Eval('state').in_(['draft'])},
        depends=['state'])
    local_transport = fields.One2Many(
        'exam_section.ta_da.local_transport',
        'ta_da',
        'Local Transport',
        states={'readonly': ~Eval('state').in_(['draft'])},
        depends=['state'])
    total_journey = fields.Function(fields.Float('Total Journey Amount'),
                                    'get_total_journey_amount')
    total_hotel_food = fields.Function(fields.Float('Total Hotel/Food Amount'),
                                       'get_total_hotel_food_amount')
    total_local_transport = fields.Function(
        fields.Float('Total Local Transport Amount'),
        'get_total_local_transport_amount')
    total_amount = fields.Function(fields.Float('Total Amount'),
                                   'get_total_amount')
    recovery = fields.Function(fields.Float('Recovery'), 'calculate_recovery')
    net_paid = fields.Function(fields.Float('Net Paid'), 'calculate_net_paid')
    # signatures = fields.One2Many(
    #     'exam_section.ta_da_signature',
    #     'ta_da',
    #     'Signature',
    #     states={
    #         'readonly': ~Eval('state').in_(['draft'])
    #     },
    #     depends=['state']
    # )
    exam = fields.Many2One('exam_section.exam',
                           'Exam',
                           states={'readonly': ~Eval('state').in_(['draft'])},
                           depends=['state'],
                           required=True)

    # @fields.depends('employee')
    # def on_change_with_designation(self):
    #     return self.employee.designation.name if self.employee else ''

    # @fields.depends('employee')
    # def on_change_with_department(self):
    #     return self.employee.department.name if self.employee else ''

    @property
    def total_hotel_amount(self):
        res = 0
        if self.hotel_food:
            for record in self.hotel_food:
                if record.type_ in ['hotel']:
                    res += record.amount
        return res

    @property
    def total_food_amount(self):
        res = 0
        if self.hotel_food:
            for record in self.hotel_food:
                if record.type_ in ['food']:
                    res += record.amount
        return res

    @staticmethod
    def default_state():
        return 'draft'

    @classmethod
    def __setup__(cls):
        '''Setup workflow transitions and button properties
           when an instance of this class is initialized'''
        super().__setup__()
        cls._buttons.update({
            'submit': {
                'invisible': ~Eval('state').in_(['draft'])
            },
            'send_for_aao_approval': {
                'invisible': ~Eval('state').in_(['confirm'])
            },
            'send_for_ao_approval': {
                'invisible': ~Eval('state').in_(['aao_approval'])
            },
            'send_for_ace_approval': {
                'invisible': ~Eval('state').in_(['ao_approval'])
            },
            'send_for_adean_approval': {
                'invisible': ~Eval('state').in_(['ace_approval'])
            },
            'send_for_dean_approval': {
                'invisible': ~Eval('state').in_(['adean_approval'])
            },
            'send_for_ao_approval_2': {
                'invisible': ~Eval('state').in_(['dean_approval'])
            },
            'approve': {
                'invisible': ~Eval('state').in_(['ao_approval_2'])
            }
        })
        cls._transitions = set(
            (('draft', 'confirm'), ('confirm', 'aao_approval'),
             ('aao_approval', 'ao_approval'), ('ao_approval', 'ace_approval'),
             ('ace_approval', 'adean_approval'), ('adean_approval',
                                                  'dean_approval'),
             ('dean_approval', 'ao_approval_2'), ('ao_approval_2',
                                                  'approved')))

    def get_total_journey_amount(self, name):
        '''Calculate the total amount for journey 
           entered in TA/DA Bill by employee'''
        total = 0
        if self.journey:
            for journey in self.journey:
                total += journey.amount
        return total

    def get_total_hotel_food_amount(self, name):
        '''Calculate the total amount for hotel and food 
           entered in TA/DA Bill by employee'''
        total = 0
        if self.hotel_food:
            for record in self.hotel_food:
                total += record.amount
        return total

    def get_total_local_transport_amount(self, name):
        '''Calculate the total amount for hotel and food 
           entered in Hotel/Food section of TA/DA Bill by employee'''
        total = 0
        if self.local_transport:
            for record in self.local_transport:
                total += record.amount
        return total

    def get_total_amount(self, name):
        '''Calculate the total amount of TA/DA Bill'''
        return (self.total_journey + self.total_hotel_food +
                self.total_local_transport)

    def calculate_recovery(self, name):
        '''Calculate the recovery for hotel stay, food and journey 
           entered in TA/DA Bill by employee'''
        global days_hotel
        global days_food
        global hotel_entitlement
        global food_entitlement
        days_hotel = 0
        days_food = 0
        hotel_entitlement = 0
        food_entitlement = 0
        res = 0
        ta_da_lines = self.exam.exam_type.ta_da
        employee_grade_pay = float(self.employee.grade_pay.name)
        for line in ta_da_lines:
            if self.employee.employee_group == line.group:
                for grade_pay in line.grade_pays:
                    if employee_grade_pay >= float(grade_pay.grade_pay.name):
                        hotel_entitlement = line.hotel_charges
                        food_entitlement = line.food_charges
        for hotel_food in self.hotel_food:
            days_hotel += hotel_food.no_of_nights_stayed \
                        if hotel_food.type_ in ['hotel'] else 0
            days_food += hotel_food.no_of_days_food \
                        if hotel_food.type_ in ['food'] else 0
        total_hotel = days_hotel * hotel_entitlement
        total_food = days_food * food_entitlement
        hotel_amount = self.total_hotel_amount
        food_amount = self.total_food_amount
        recovery_hotel = total_hotel - hotel_amount \
                        if total_hotel > hotel_amount else 0
        recovery_food = total_food - food_amount \
                        if total_food > food_amount else 0
        res = recovery_food + recovery_hotel
        return res

    def calculate_net_paid(self, name):
        '''Calculate net paid amount, i.e.
           total amount - recovery amount'''
        return self.total_amount - self.recovery

    @property
    def total_hotel_amount(self):
        '''Calculate the total amount for hotel stay entered in
           Hotel/Food section of TA/DA Bill by employee'''
        res = 0
        if self.hotel_food:
            for record in self.hotel_food:
                if record.type_ in ['hotel']:
                    res += record.amount
        return res

    @property
    def total_food_amount(self):
        '''Calculate the total amount for food entries entered in
           Hotel/Food section of TA/DA Bill by employee'''
        res = 0
        if self.hotel_food:
            for record in self.hotel_food:
                if record.type_ in ['food']:
                    res += record.amount
        return res

    @classmethod
    @Workflow.transition('confirm')
    def submit(cls, records):
        '''Change status of bill to confirm
           Set submit date when TA/DA Bill is submitted'''
        for record in records:
            record.submit_date = date.today()
            record.save()

    '''Button functions for executing workflow transitions'''

    @classmethod
    @Workflow.transition('aao_approval')
    def send_for_aao_approval(cls, records):
        '''Change status of bill to aao_approval'''
        pass

    @classmethod
    @Workflow.transition('ao_approval')
    def send_for_ao_approval(cls, records):
        '''Change status of bill to ao_approval'''
        pass

    @classmethod
    @Workflow.transition('ace_approval')
    def send_for_ace_approval(cls, records):
        '''Change status of bill to ace_approval'''
        pass

    @classmethod
    @Workflow.transition('adean_approval')
    def send_for_adean_approval(cls, records):
        '''Change status of bill to adean_approval'''
        pass

    @classmethod
    @Workflow.transition('dean_approval')
    def send_for_dean_approval(cls, records):
        '''Change status of bill to dean_approval'''
        pass

    @classmethod
    @Workflow.transition('ao_approval_2')
    def send_for_ao_approval_2(cls, records):
        '''Change status of bill to ao_approval_2'''
        pass

    @classmethod
    @Workflow.transition('approved')
    def approve(cls, records):
        '''Set approved date when TA/DA Bill is approved'''
        for record in records:
            record.approved_date = date.today()
            record.save()
Esempio n. 8
0
class ModelViewStoredChangedValuesTarget(ModelSQL, ModelView):
    "ModelSQL Stored Changed Values Target"
    __name__ = 'test.modelview.stored.changed_values.target'
    name = fields.Char("Name")
    parent = fields.Many2One('test.modelview.stored.changed_values', "Parent")
Esempio n. 9
0
class PurchaseRequest(ModelSQL, ModelView):
    'Purchase Request'
    __name__ = 'purchase.request'
    product = fields.Many2One('product.product',
                              'Product',
                              required=True,
                              select=True,
                              readonly=True,
                              domain=[('purchasable', '=', True)])
    party = fields.Many2One('party.party',
                            'Party',
                            select=True,
                            states=STATES,
                            depends=DEPENDS)
    quantity = fields.Float('Quantity',
                            required=True,
                            states=STATES,
                            digits=(16, Eval('uom_digits', 2)),
                            depends=DEPENDS + ['uom_digits'])
    uom = fields.Many2One('product.uom',
                          'UOM',
                          required=True,
                          select=True,
                          states=STATES,
                          depends=DEPENDS)
    uom_digits = fields.Function(fields.Integer('UOM Digits'),
                                 'on_change_with_uom_digits')
    computed_quantity = fields.Float('Computed Quantity', readonly=True)
    computed_uom = fields.Many2One('product.uom',
                                   'Computed UOM',
                                   readonly=True)
    purchase_date = fields.Date('Best Purchase Date', readonly=True)
    supply_date = fields.Date('Expected Supply Date', readonly=True)
    default_uom_digits = fields.Function(fields.Integer('Default UOM Digits'),
                                         'on_change_with_default_uom_digits')
    stock_level = fields.Float('Stock at Supply Date',
                               readonly=True,
                               digits=(16, Eval('default_uom_digits', 2)),
                               depends=['default_uom_digits'])
    warehouse = fields.Many2One('stock.location',
                                "Warehouse",
                                states={
                                    'required': Eval('warehouse_required',
                                                     False),
                                },
                                domain=[('type', '=', 'warehouse')],
                                depends=['warehouse_required'],
                                readonly=True)
    warehouse_required = fields.Function(fields.Boolean('Warehouse Required'),
                                         'get_warehouse_required')
    purchase_line = fields.Many2One('purchase.line',
                                    'Purchase Line',
                                    readonly=True)
    purchase = fields.Function(
        fields.Many2One('purchase.purchase', 'Purchase'), 'get_purchase')
    company = fields.Many2One('company.company',
                              'Company',
                              required=True,
                              readonly=True,
                              domain=[
                                  ('id',
                                   If(In('company', Eval('context', {})), '=',
                                      '!='), Eval('context',
                                                  {}).get('company', -1)),
                              ])
    origin = fields.Reference('Origin',
                              selection='get_origin',
                              readonly=True,
                              required=True)
    state = fields.Function(fields.Selection([
        ('purchased', 'Purchased'),
        ('done', 'Done'),
        ('draft', 'Draft'),
        ('cancel', 'Cancel'),
    ], 'State'),
                            'get_state',
                            searcher='search_state')

    @classmethod
    def __setup__(cls):
        super(PurchaseRequest, cls).__setup__()
        cls._order[0] = ('id', 'DESC')
        cls._error_messages.update({
            'create_request': ('Purchase requests are only created '
                               'by the system.'),
            'delete_purchase_line': ('You can not delete purchased '
                                     'request.'),
        })

    @classmethod
    def __register__(cls, module_name):
        TableHandler = backend.get('TableHandler')
        cursor = Transaction().cursor
        sql_table = cls.__table__()
        super(PurchaseRequest, cls).__register__(module_name)

        # Migration from 2.0: empty order point origin is -1 instead of 0
        cursor.execute(
            *sql_table.update(columns=[sql_table.origin],
                              values=['stock.order_point,-1'],
                              where=sql_table.origin == 'stock.order_point,0'))

        # Migration from 3.6: removing the constraint on the quantity
        tablehandler = TableHandler(cursor, cls, module_name)
        tablehandler.drop_constraint('check_purchase_request_quantity')

    def get_rec_name(self, name):
        if self.warehouse:
            return "%s@%s" % (self.product.name, self.warehouse.name)
        else:
            return self.product.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(('warehouse', clause[1], names[1]))
        return res

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

    def get_purchase(self, name):
        if self.purchase_line:
            return self.purchase_line.purchase.id

    @property
    def currency(self):
        return self.company.currency

    def get_state(self, name):
        if self.purchase_line:
            if self.purchase_line.purchase.state == 'cancel':
                return 'cancel'
            elif self.purchase_line.purchase.state == 'done':
                return 'done'
            else:
                return 'purchased'
        return 'draft'

    @classmethod
    def search_state(cls, name, clause):
        pool = Pool()
        Purchase = pool.get('purchase.purchase')
        PurchaseLine = pool.get('purchase.line')

        request = cls.__table__()
        purchase_line = PurchaseLine.__table__()
        purchase = Purchase.__table__()

        _, operator_, state = clause
        Operator = fields.SQL_OPERATORS[operator_]
        state_case = Case((purchase.state == 'cancel', 'cancel'),
                          (purchase.state == 'done', 'done'),
                          (request.purchase_line != Null, 'purchased'),
                          else_='draft')
        state_query = request.join(
            purchase_line,
            type_='LEFT',
            condition=request.purchase_line == purchase_line.id).join(
                purchase,
                type_='LEFT',
                condition=purchase_line.purchase == purchase.id).select(
                    request.id, where=Operator(state_case, state))

        return [('id', 'in', state_query)]

    def get_warehouse_required(self, name):
        return self.product.type in ('goods', 'assets')

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

    @fields.depends('product')
    def on_change_with_default_uom_digits(self, name=None):
        if self.product:
            return self.product.default_uom.digits
        return 2

    @staticmethod
    def _get_origin():
        'Return the set of Model names for origin Reference'
        return {'stock.order_point'}

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

    @classmethod
    def generate_requests(cls, products=None, warehouses=None):
        """
        For each product compute the purchase request that must be
        created today to meet product outputs.

        If products is specified it will compute the purchase requests
        for the selected products.

        If warehouses is specified it will compute the purchase request
        necessary for the selected warehouses.
        """
        pool = Pool()
        OrderPoint = pool.get('stock.order_point')
        Product = pool.get('product.product')
        Location = pool.get('stock.location')
        User = pool.get('res.user')
        company = User(Transaction().user).company

        if warehouses is None:
            # fetch warehouses:
            warehouses = Location.search([
                ('type', '=', 'warehouse'),
            ])
        warehouse_ids = [w.id for w in warehouses]
        # fetch order points
        order_points = OrderPoint.search([
            ('type', '=', 'purchase'),
            ('company', '=', company.id if company else None),
        ])
        # index them by product
        product2ops = {}
        for order_point in order_points:
            product2ops[(order_point.warehouse_location.id,
                         order_point.product.id)] = order_point

        if products is None:
            # fetch goods and assets
            # ordered by ids to speedup reduce_ids in products_by_location
            products = Product.search([
                ('type', 'in', ['goods', 'assets']),
                ('consumable', '=', False),
                ('purchasable', '=', True),
            ],
                                      order=[('id', 'ASC')])
        product_ids = [p.id for p in products]
        # aggregate product by minimum supply date
        date2products = {}
        for product in products:
            min_date, max_date = cls.get_supply_dates(product)
            date2products.setdefault((min_date, max_date), []).append(product)

        # compute requests
        new_requests = []
        for dates, dates_products in date2products.iteritems():
            min_date, max_date = dates
            for sub_products in grouped_slice(dates_products):
                sub_products = list(sub_products)
                product_ids = [p.id for p in sub_products]
                with Transaction().set_context(forecast=True,
                                               stock_date_end=min_date
                                               or datetime.date.max):
                    pbl = Product.products_by_location(warehouse_ids,
                                                       product_ids,
                                                       with_childs=True)
                for warehouse_id in warehouse_ids:
                    min_date_qties = dict((x, pbl.pop((warehouse_id, x), 0))
                                          for x in product_ids)
                    # Search for shortage between min-max
                    shortages = cls.get_shortage(warehouse_id,
                                                 product_ids,
                                                 min_date,
                                                 max_date,
                                                 min_date_qties=min_date_qties,
                                                 order_points=product2ops)

                    for product in sub_products:
                        shortage_date, product_quantity = shortages[product.id]
                        if shortage_date is None or product_quantity is None:
                            continue
                        order_point = product2ops.get(
                            (warehouse_id, product.id))
                        # generate request values
                        request = cls.compute_request(product, warehouse_id,
                                                      shortage_date,
                                                      product_quantity,
                                                      company, order_point)
                        new_requests.append(request)

        # delete purchase requests without a purchase line
        products = set(products)
        reqs = cls.search([
            ('purchase_line', '=', None),
            ('origin', 'like', 'stock.order_point,%'),
        ])
        reqs = [r for r in reqs if r.product in products]
        cls.delete(reqs)
        new_requests = cls.compare_requests(new_requests)

        cls.create_requests(new_requests)

    @classmethod
    def create_requests(cls, new_requests):
        for new_req in new_requests:
            if new_req.supply_date == datetime.date.max:
                new_req.supply_date = None
            if new_req.computed_quantity > 0:
                new_req.save()

    @classmethod
    def compare_requests(cls, new_requests):
        """
        Compare new_requests with already existing request to avoid
        to re-create existing requests.
        """
        pool = Pool()
        Uom = pool.get('product.uom')
        Request = pool.get('purchase.request')

        requests = Request.search([
            ('purchase_line.moves', '=', None),
            ('purchase_line.purchase.state', '!=', 'cancel'),
            ('origin', 'like', 'stock.order_point,%'),
        ])
        # Fetch data from existing requests
        existing_req = {}
        for request in requests:
            pline = request.purchase_line
            # Skip incoherent request
            if request.product.id != pline.product.id or \
                    request.warehouse.id != pline.purchase.warehouse.id:
                continue
            # Take smallest amount between request and purchase line
            pline_qty = Uom.compute_qty(pline.unit,
                                        pline.quantity,
                                        pline.product.default_uom,
                                        round=False)
            quantity = min(request.computed_quantity, pline_qty)

            existing_req.setdefault(
                (request.product.id, request.warehouse.id), []).append({
                    'supply_date': (request.supply_date or datetime.date.max),
                    'quantity':
                    quantity,
                })

        for i in existing_req.itervalues():
            i.sort(lambda r, s: cmp(r['supply_date'], s['supply_date']))

        # Update new requests to take existing requests into account
        new_requests.sort(key=operator.attrgetter('supply_date'))
        for new_req in new_requests:
            for old_req in existing_req.get(
                (new_req.product.id, new_req.warehouse.id), []):
                if old_req['supply_date'] <= new_req.supply_date:
                    new_req.computed_quantity = max(
                        0.0, new_req.computed_quantity - old_req['quantity'])
                    new_req.quantity = Uom.compute_qty(
                        new_req.product.default_uom, new_req.computed_quantity,
                        new_req.uom)
                    old_req['quantity'] = max(
                        0.0, old_req['quantity'] - new_req.computed_quantity)
                else:
                    break

        return new_requests

    @classmethod
    def get_supply_dates(cls, product):
        """
        Return the minimal interval of earliest supply dates for a product.
        """
        Date = Pool().get('ir.date')

        min_date = None
        max_date = None
        today = Date.today()

        for product_supplier in product.product_suppliers:
            supply_date = product_supplier.compute_supply_date(date=today)
            # TODO next_day is by default today + 1 but should depends
            # on the CRON activity
            next_day = today + datetime.timedelta(1)
            next_supply_date = product_supplier.compute_supply_date(
                date=next_day)
            if (not min_date) or supply_date < min_date:
                min_date = supply_date
            if (not max_date):
                max_date = next_supply_date
            if supply_date > min_date and supply_date < max_date:
                max_date = supply_date
            if next_supply_date < max_date:
                max_date = next_supply_date

        if not min_date:
            min_date = datetime.date.max
            max_date = datetime.date.max

        return (min_date, max_date)

    @classmethod
    def find_best_supplier(cls, product, date):
        '''
        Return the best supplier and purchase_date for the product.
        '''
        Date = Pool().get('ir.date')

        supplier = None
        today = Date.today()
        for product_supplier in product.product_suppliers:
            supply_date = product_supplier.compute_supply_date(date=today)
            timedelta = date - supply_date
            if not supplier and timedelta >= datetime.timedelta(0):
                supplier = product_supplier.party
                break

        if supplier:
            purchase_date = product_supplier.compute_purchase_date(date)
        else:
            purchase_date = today
        return supplier, purchase_date

    @classmethod
    def compute_request(cls,
                        product,
                        location_id,
                        shortage_date,
                        product_quantity,
                        company,
                        order_point=None):
        """
        Return the value of the purchase request which will answer to
        the needed quantity at the given date. I.e: the latest
        purchase date, the expected supply date and the prefered
        supplier.
        """
        pool = Pool()
        Uom = pool.get('product.uom')
        Request = pool.get('purchase.request')

        supplier, purchase_date = cls.find_best_supplier(
            product, shortage_date)

        max_quantity = order_point and order_point.max_quantity or 0.0
        computed_quantity = max_quantity - product_quantity
        quantity = Uom.compute_qty(product.default_uom, computed_quantity,
                                   product.purchase_uom or product.default_uom)

        if order_point:
            origin = 'stock.order_point,%s' % order_point.id
        else:
            origin = 'stock.order_point,-1'
        return Request(
            product=product,
            party=supplier and supplier or None,
            quantity=quantity,
            uom=product.purchase_uom or product.default_uom,
            computed_quantity=computed_quantity,
            computed_uom=product.default_uom,
            purchase_date=purchase_date,
            supply_date=shortage_date,
            stock_level=product_quantity,
            company=company,
            warehouse=location_id,
            origin=origin,
        )

    @classmethod
    def get_shortage(cls, location_id, product_ids, min_date, max_date,
                     min_date_qties, order_points):
        """
        Return for each product the first date between min_date and max_date
        where the stock quantity is less than the minimal quantity and the
        smallest stock quantity in the interval or None if there is no date
        where stock quantity is less than the minimal quantity.

        The minimal quantity comes from the order point or is zero.

        min_date_qty is the quantities for each products at the min_date.
        order_points is a dictionary that links products to order point.
        """
        Product = Pool().get('product.product')

        res_dates = {}
        res_qties = {}

        min_quantities = {}
        for product_id in product_ids:
            order_point = order_points.get((location_id, product_id))
            if order_point:
                min_quantities[product_id] = order_point.min_quantity
            else:
                min_quantities[product_id] = 0.0

        current_date = min_date
        current_qties = min_date_qties.copy()
        while (current_date < max_date) or (current_date == min_date):
            for product_id in product_ids:
                current_qty = current_qties[product_id]
                min_quantity = min_quantities[product_id]
                res_qty = res_qties.get(product_id)
                res_date = res_dates.get(product_id)
                if current_qty < min_quantity:
                    if not res_date:
                        res_dates[product_id] = current_date
                    if (not res_qty) or (current_qty < res_qty):
                        res_qties[product_id] = current_qty

            if current_date == datetime.date.max:
                break
            current_date += datetime.timedelta(1)

            # Update current quantities with next moves
            with Transaction().set_context(forecast=True,
                                           stock_date_start=current_date,
                                           stock_date_end=current_date):
                pbl = Product.products_by_location([location_id],
                                                   product_ids,
                                                   with_childs=True)
            for key, qty in pbl.iteritems():
                _, product_id = key
                current_qties[product_id] += qty

        return dict(
            (x, (res_dates.get(x), res_qties.get(x))) for x in product_ids)

    @classmethod
    def create(cls, vlist):
        for vals in vlist:
            for field_name in ('product', 'quantity', 'uom', 'company'):
                if not vals.get(field_name):
                    cls.raise_user_error('create_request')
        return super(PurchaseRequest, cls).create(vlist)

    @classmethod
    def delete(cls, requests):
        if any(r.purchase_line for r in requests):
            cls.raise_user_error('delete_purchase_line')
        super(PurchaseRequest, cls).delete(requests)
Esempio n. 10
0
class Journal(metaclass=PoolMeta):
    __name__ = 'account.payment.journal'
    company_party = fields.Function(
        fields.Many2One('party.party', 'Company Party'),
        'on_change_with_company_party')
    sepa_bank_account_number = fields.Many2One(
        'bank.account.number',
        'Bank Account Number',
        states={
            'required': Eval('process_method') == 'sepa',
            'invisible': Eval('process_method') != 'sepa',
        },
        domain=[
            ('type', '=', 'iban'),
            ('account.owners', '=', Eval('company_party')),
        ],
        depends=['process_method', 'company_party'])
    sepa_payable_flavor = fields.Selection(
        [
            (None, ''),
            ('pain.001.001.03', 'pain.001.001.03'),
            ('pain.001.001.05', 'pain.001.001.05'),
            ('pain.001.003.03', 'pain.001.003.03'),
        ],
        'Payable Flavor',
        states={
            'required': Eval('process_method') == 'sepa',
            'invisible': Eval('process_method') != 'sepa',
        },
        translate=False,
        depends=['process_method'])
    sepa_receivable_flavor = fields.Selection(
        [
            (None, ''),
            ('pain.008.001.02', 'pain.008.001.02'),
            ('pain.008.001.04', 'pain.008.001.04'),
            ('pain.008.003.02', 'pain.008.003.02'),
        ],
        'Receivable Flavor',
        states={
            'required': Eval('process_method') == 'sepa',
            'invisible': Eval('process_method') != 'sepa',
        },
        translate=False,
        depends=['process_method'])
    sepa_batch_booking = fields.Boolean('Batch Booking',
                                        states={
                                            'invisible':
                                            Eval('process_method') != 'sepa',
                                        },
                                        depends=['process_method'])
    sepa_charge_bearer = fields.Selection(
        [
            ('DEBT', 'Debtor'),
            ('CRED', 'Creditor'),
            ('SHAR', 'Shared'),
            ('SLEV', 'Service Level'),
        ],
        'Charge Bearer',
        states={
            'required': Eval('process_method') == 'sepa',
            'invisible': Eval('process_method') != 'sepa',
        },
        depends=['process_method'])

    @classmethod
    def __setup__(cls):
        super(Journal, cls).__setup__()
        sepa_method = ('sepa', 'SEPA')
        if sepa_method not in cls.process_method.selection:
            cls.process_method.selection.append(sepa_method)

    @classmethod
    def default_company_party(cls):
        pool = Pool()
        Company = pool.get('company.company')
        company_id = cls.default_company()
        if company_id:
            return Company(company_id).party.id

    @fields.depends('company')
    def on_change_with_company_party(self, name=None):
        if self.company:
            return self.company.party.id

    @staticmethod
    def default_sepa_charge_bearer():
        return 'SLEV'
Esempio n. 11
0
class Message(Workflow, ModelSQL, ModelView):
    'SEPA Message'
    __name__ = 'account.payment.sepa.message'
    _states = {
        'readonly': Eval('state') != 'draft',
    }
    _depends = ['state']
    message = fields.Text('Message', states=_states, depends=_depends)
    filename = fields.Function(fields.Char('Filename'), 'get_filename')
    type = fields.Selection([
        ('in', 'IN'),
        ('out', 'OUT'),
    ],
                            'Type',
                            required=True,
                            states=_states,
                            depends=_depends)
    company = fields.Many2One(
        'company.company',
        'Company',
        required=True,
        select=True,
        domain=[
            ('id', If(Eval('context', {}).contains('company'), '=',
                      '!='), Eval('context', {}).get('company', -1)),
        ],
        states={
            'readonly': Eval('state') != 'draft',
        },
        depends=['state'])
    origin = fields.Reference('Origin',
                              selection='get_origin',
                              select=True,
                              states=_states,
                              depends=_depends)
    state = fields.Selection([
        ('draft', 'Draft'),
        ('waiting', 'Waiting'),
        ('done', 'Done'),
        ('canceled', 'Canceled'),
    ],
                             'State',
                             readonly=True,
                             select=True)

    @classmethod
    def __setup__(cls):
        super(Message, cls).__setup__()
        cls._transitions |= {
            ('draft', 'waiting'),
            ('waiting', 'done'),
            ('waiting', 'draft'),
            ('draft', 'canceled'),
            ('waiting', 'canceled'),
        }
        cls._buttons.update({
            'cancel': {
                'invisible': ~Eval('state').in_(['draft', 'waiting']),
                'depends': ['state'],
            },
            'draft': {
                'invisible': Eval('state') != 'waiting',
                'depends': ['state'],
            },
            'wait': {
                'invisible': Eval('state') != 'draft',
                'depends': ['state'],
            },
            'do': {
                'invisible': Eval('state') != 'waiting',
                'depends': ['state'],
            },
        })

    @classmethod
    def __register__(cls, module_name):
        TableHandler = backend.get('TableHandler')
        cursor = Transaction().connection.cursor()
        pool = Pool()
        Group = pool.get('account.payment.group')

        super(Message, cls).__register__(module_name)

        # Migration from 3.2
        if TableHandler.table_exist(Group._table):
            group_table = Group.__table_handler__(module_name)
            if group_table.column_exist('sepa_message'):
                group = Group.__table__()
                table = cls.__table__()
                cursor.execute(
                    *group.select(group.id, group.sepa_message, group.company))
                for group_id, message, company_id in cursor.fetchall():
                    cursor.execute(*table.insert([
                        table.message, table.type, table.company, table.origin,
                        table.state
                    ], [[
                        message, 'out', company_id,
                        'account.payment.group,%s' % group_id, 'done'
                    ]]))
                group_table.drop_column('sepa_message')

    @staticmethod
    def default_type():
        return 'in'

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

    @staticmethod
    def default_state():
        return 'draft'

    def get_filename(self, name):
        pool = Pool()
        Group = pool.get('account.payment.group')
        if isinstance(self.origin, Group):
            return self.origin.rec_name + '.xml'

    @staticmethod
    def _get_origin():
        'Return list of Model names for origin Reference'
        return ['account.payment.group']

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

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

    @classmethod
    @ModelView.button
    @Workflow.transition('waiting')
    def wait(cls, messages):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('done')
    def do(cls, messages):
        for message in messages:
            if message.type == 'in':
                message.parse()
            else:
                message.send()

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

    @staticmethod
    def _get_handlers():
        pool = Pool()
        Payment = pool.get('account.payment')
        return {
            'urn:iso:std:iso:20022:tech:xsd:camt.054.001.01':
            lambda f: CAMT054(f, Payment),
            'urn:iso:std:iso:20022:tech:xsd:camt.054.001.02':
            lambda f: CAMT054(f, Payment),
            'urn:iso:std:iso:20022:tech:xsd:camt.054.001.03':
            lambda f: CAMT054(f, Payment),
            'urn:iso:std:iso:20022:tech:xsd:camt.054.001.04':
            lambda f: CAMT054(f, Payment),
        }

    @staticmethod
    def get_namespace(message):
        f = BytesIO(message)
        for _, element in etree.iterparse(f, events=('start', )):
            tag = etree.QName(element)
            if tag.localname == 'Document':
                return tag.namespace

    def parse(self):
        message = self.message.encode('utf-8')
        f = BytesIO(message)
        namespace = self.get_namespace(message)
        handlers = self._get_handlers()
        if namespace not in handlers:
            raise  # TODO UserError
        handlers[namespace](f)

    def send(self):
        pass
Esempio n. 12
0
class Mandate(Workflow, ModelSQL, ModelView):
    'SEPA Mandate'
    __name__ = 'account.payment.sepa.mandate'
    party = fields.Many2One('party.party',
                            'Party',
                            required=True,
                            select=True,
                            states={
                                'readonly':
                                Eval('state').in_(
                                    ['requested', 'validated', 'canceled']),
                            },
                            depends=['state'])
    account_number = fields.Many2One(
        'bank.account.number',
        'Account Number',
        ondelete='RESTRICT',
        states={
            'readonly': Eval('state').in_(['validated', 'canceled']),
            'required': Eval('state') == 'validated',
        },
        domain=[
            ('type', '=', 'iban'),
            ('account.owners', '=', Eval('party')),
        ],
        depends=['state', 'party'])
    identification = fields.Char('Identification',
                                 size=35,
                                 states={
                                     'readonly':
                                     Eval('identification_readonly', True),
                                     'required':
                                     Eval('state') == 'validated',
                                 },
                                 depends=['state', 'identification_readonly'])
    identification_readonly = fields.Function(
        fields.Boolean('Identification Readonly'),
        'get_identification_readonly')
    company = fields.Many2One(
        'company.company',
        'Company',
        required=True,
        select=True,
        domain=[
            ('id', If(Eval('context', {}).contains('company'), '=',
                      '!='), Eval('context', {}).get('company', -1)),
        ],
        states={
            'readonly': Eval('state') != 'draft',
        },
        depends=['state'])
    type = fields.Selection([
        ('recurrent', 'Recurrent'),
        ('one-off', 'One-off'),
    ],
                            'Type',
                            states={
                                'readonly':
                                Eval('state').in_(['validated', 'canceled']),
                            },
                            depends=['state'])
    sequence_type_rcur = fields.Boolean("Always use RCUR",
                                        states={
                                            'invisible':
                                            Eval('type') == 'one-off',
                                        },
                                        depends=['type'])
    scheme = fields.Selection([
        ('CORE', 'Core'),
        ('B2B', 'Business to Business'),
    ],
                              'Scheme',
                              required=True,
                              states={
                                  'readonly':
                                  Eval('state').in_(['validated', 'canceled']),
                              },
                              depends=['state'])
    scheme_string = scheme.translated('scheme')
    signature_date = fields.Date('Signature Date',
                                 states={
                                     'readonly':
                                     Eval('state').in_(
                                         ['validated', 'canceled']),
                                     'required':
                                     Eval('state') == 'validated',
                                 },
                                 depends=['state'])
    state = fields.Selection([
        ('draft', 'Draft'),
        ('requested', 'Requested'),
        ('validated', 'Validated'),
        ('canceled', 'Canceled'),
    ],
                             'State',
                             readonly=True)
    payments = fields.One2Many('account.payment', 'sepa_mandate', 'Payments')
    has_payments = fields.Function(fields.Boolean('Has Payments'),
                                   'has_payments')

    @classmethod
    def __setup__(cls):
        super(Mandate, cls).__setup__()
        cls._transitions |= set((
            ('draft', 'requested'),
            ('requested', 'validated'),
            ('validated', 'canceled'),
            ('requested', 'canceled'),
            ('requested', 'draft'),
        ))
        cls._buttons.update({
            'cancel': {
                'invisible': ~Eval('state').in_(['requested', 'validated']),
                'depends': ['state'],
            },
            'draft': {
                'invisible': Eval('state') != 'requested',
                'depends': ['state'],
            },
            'request': {
                'invisible': Eval('state') != 'draft',
                'depends': ['state'],
            },
            'validate_mandate': {
                'invisible': Eval('state') != 'requested',
                'depends': ['state'],
            },
        })
        # t = cls.__table__()
        # JMO/RSE 2017_04_18 Following 4929e02594d
        # We override in coog the possibility to keep the mandate
        # for several bank account but we suffer from the register order
        # if we try to delete the constraint from coog module
        # t = cls.__table__()
        # cls._sql_constraints = [
        #     ('identification_unique', Unique(t, t.company, t.identification),
        #         'account_payment_sepa.msg_mandate_unique_id'),
        #     ]

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

    @staticmethod
    def default_type():
        return 'recurrent'

    @classmethod
    def default_sequence_type_rcur(cls):
        return False

    @staticmethod
    def default_scheme():
        return 'CORE'

    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def default_identification_readonly():
        pool = Pool()
        Configuration = pool.get('account.configuration')
        config = Configuration(1)
        return bool(config.sepa_mandate_sequence)

    def get_identification_readonly(self, name):
        return bool(self.identification)

    def get_rec_name(self, name):
        if self.identification:
            return self.identification
        return '(%s)' % self.id

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

    @classmethod
    def create(cls, vlist):
        pool = Pool()
        Sequence = pool.get('ir.sequence')
        Configuration = pool.get('account.configuration')

        config = Configuration(1)
        vlist = [v.copy() for v in vlist]
        for values in vlist:
            if (config.sepa_mandate_sequence
                    and not values.get('identification')):
                values['identification'] = Sequence.get_id(
                    config.sepa_mandate_sequence.id)
            # Prevent raising false unique constraint
            if values.get('identification') == '':
                values['identification'] = None
        return super(Mandate, cls).create(vlist)

    @classmethod
    def write(cls, *args):
        actions = iter(args)
        args = []
        for mandates, values in zip(actions, actions):
            # Prevent raising false unique constraint
            if values.get('identification') == '':
                values = values.copy()
                values['identification'] = None
            args.extend((mandates, values))
        super(Mandate, cls).write(*args)

    @classmethod
    def copy(cls, mandates, default=None):
        if default is None:
            default = {}
        else:
            default = default.copy()
        default.setdefault('payments', [])
        default.setdefault('signature_date', None)
        default.setdefault('identification', None)
        return super(Mandate, cls).copy(mandates, default=default)

    @property
    def is_valid(self):
        if self.state == 'validated':
            if self.type == 'one-off':
                if not self.has_payments:
                    return True
            else:
                return True
        return False

    @property
    def sequence_type(self):
        if self.type == 'one-off':
            return 'OOFF'
        elif not self.sequence_type_rcur and (not self.payments or all(
                not p.sepa_mandate_sequence_type
                for p in self.payments) or all(p.rejected
                                               for p in self.payments)):
            return 'FRST'
        # TODO manage FNAL
        else:
            return 'RCUR'

    @classmethod
    def has_payments(cls, mandates, name):
        pool = Pool()
        Payment = pool.get('account.payment')
        payment = Payment.__table__
        cursor = Transaction().connection.cursor()

        has_payments = dict.fromkeys([m.id for m in mandates], False)
        for sub_ids in grouped_slice(mandates):
            red_sql = reduce_ids(payment.sepa_mandate, sub_ids)
            cursor.execute(*payment.select(payment.sepa_mandate,
                                           Literal(True),
                                           where=red_sql,
                                           group_by=payment.sepa_mandate))
            has_payments.update(cursor.fetchall())

        return {'has_payments': has_payments}

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

    @classmethod
    @ModelView.button
    @Workflow.transition('requested')
    def request(cls, mandates):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('validated')
    def validate_mandate(cls, mandates):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('canceled')
    def cancel(cls, mandates):
        # TODO must be automaticaly canceled 13 months after last collection
        pass

    @classmethod
    def delete(cls, mandates):
        for mandate in mandates:
            if mandate.state not in ('draft', 'canceled'):
                raise AccessError(
                    gettext(
                        'account_payment_sepa'
                        '.msg_mandate_delete_draft_canceled',
                        mandate=mandate.rec_name))
        super(Mandate, cls).delete(mandates)
Esempio n. 13
0
class Payment(metaclass=PoolMeta):
    __name__ = 'account.payment'

    sepa_mandate = fields.Many2One('account.payment.sepa.mandate',
                                   'Mandate',
                                   ondelete='RESTRICT',
                                   domain=[
                                       ('party', '=', Eval('party', -1)),
                                       ('company', '=', Eval('company', -1)),
                                   ],
                                   depends=['party', 'company'])
    sepa_mandate_sequence_type = fields.Char('Mandate Sequence Type',
                                             readonly=True)
    sepa_return_reason_code = fields.Char('Return Reason Code',
                                          readonly=True,
                                          states={
                                              'invisible':
                                              (~Eval('sepa_return_reason_code')
                                               & (Eval('state') != 'failed')),
                                          },
                                          depends=['state'])
    sepa_return_reason_information = fields.Text(
        'Return Reason Information',
        readonly=True,
        states={
            'invisible': (~Eval('sepa_return_reason_information')
                          & (Eval('state') != 'failed')),
        },
        depends=['state'])
    sepa_end_to_end_id = fields.Function(fields.Char('SEPA End To End ID'),
                                         'get_sepa_end_to_end_id',
                                         searcher='search_end_to_end_id')
    sepa_instruction_id = fields.Function(
        fields.Char('SEPA Instruction ID'),
        'get_sepa_instruction_id',
        searcher='search_sepa_instruction_id')

    @classmethod
    def copy(cls, payments, default=None):
        if default is None:
            default = {}
        else:
            default = default.copy()
        default.setdefault('sepa_mandate_sequence_type', None)
        return super(Payment, cls).copy(payments, default=default)

    @classmethod
    def get_sepa_mandates(cls, payments):
        mandates = []
        for payment in payments:
            if payment.sepa_mandate:
                if payment.sepa_mandate.is_valid:
                    mandate = payment.sepa_mandate
                else:
                    mandate = None
            else:
                for mandate in payment.party.sepa_mandates:
                    if mandate.is_valid:
                        break
                else:
                    mandate = None
            mandates.append(mandate)
        return mandates

    def get_sepa_end_to_end_id(self, name):
        return str(self.id)

    @classmethod
    def search_end_to_end_id(cls, name, domain):
        table = cls.__table__()
        _, operator, value = domain
        cast = cls.sepa_end_to_end_id._field.sql_type().base
        Operator = fields.SQL_OPERATORS[operator]
        query = table.select(table.id,
                             where=Operator(table.id.cast(cast), value))
        return [('id', 'in', query)]

    get_sepa_instruction_id = get_sepa_end_to_end_id
    search_sepa_instruction_id = search_end_to_end_id

    @property
    def sepa_remittance_information(self):
        if self.description:
            return self.description
        elif self.line and self.line.move_origin:
            return self.line.move_origin.rec_name

    @property
    def sepa_bank_account_number(self):
        if self.kind == 'receivable':
            if self.sepa_mandate:
                return self.sepa_mandate.account_number
        else:
            for account in self.party.bank_accounts:
                for number in account.numbers:
                    if number.type == 'iban':
                        return number

    @property
    def rejected(self):
        return (self.state == 'failed' and self.sepa_return_reason_code
                and self.sepa_return_reason_information == '/RTYP/RJCT')

    def create_clearing_move(self, date=None):
        if not date:
            date = Transaction().context.get('date_value')
        return super(Payment, self).create_clearing_move(date=date)
Esempio n. 14
0
class Party:
    __metaclass__ = PoolMeta
    __name__ = 'party.party'

    customer_payment_type = fields.Function(fields.Many2One(
        'account.payment.type',
        'Customer Payment type',
        domain=[
            ('kind', '=', 'receivable'),
        ],
        help='Payment type of the customer.'),
                                            'get_payment_type',
                                            setter='set_payment_type',
                                            searcher='search_payment_type')
    supplier_payment_type = fields.Function(fields.Many2One(
        'account.payment.type',
        string='Supplier Payment type',
        domain=[
            ('kind', '=', 'payable'),
        ],
        help='Payment type of the supplier.'),
                                            'get_payment_type',
                                            setter='set_payment_type',
                                            searcher='search_payment_type')

    @classmethod
    def get_payment_type(cls, parties, names):
        PartyAccountPaymentType = Pool().get('party.account.payment.type')
        company = Transaction().context.get('company')

        res = {}
        party_ids = [p.id for p in parties]
        for fname in names:
            res[fname] = {}.fromkeys(party_ids, None)

        party_account_payment_type = PartyAccountPaymentType.search([
            ('company', '=', company), ('party', 'in', parties)
        ])
        for payment_type_fields in party_account_payment_type:
            party_id = payment_type_fields.party.id
            for fname in set(names):
                value = getattr(payment_type_fields, fname)
                res[fname][party_id] = value and value.id
        return res

    @classmethod
    def set_payment_type(cls, parties, name, value):
        PartyAccountPaymentType = Pool().get('party.account.payment.type')
        company = Transaction().context.get('company')

        party_payment_types = PartyAccountPaymentType.search([
            ('company', '=', company), ('party', 'in', parties)
        ])
        PartyAccountPaymentType.write(party_payment_types, {name: value})

        vlist = []
        parties_done_ids = [papt.party.id for papt in party_payment_types]
        for missing_party_id in set(p.id for p in parties
                                    if p.id not in parties_done_ids):
            vlist.append({
                'party': missing_party_id,
                'company': company,
                name: value,
            })
        if vlist:
            PartyAccountPaymentType.create(vlist)

    @classmethod
    def search_payment_type(cls, name, clause):
        PartyAccountPaymentType = Pool().get('party.account.payment.type')
        company = Transaction().context.get('company')

        party_payment_types = PartyAccountPaymentType.search([
            ('company', '=', company),
            tuple(clause),
        ])
        return [('id', 'in', [papt.party.id for papt in party_payment_types])]
Esempio n. 15
0
class Surgery(ModelSQL, ModelView):
    'Surgery'
    __name__ = 'gnuhealth.surgery'

    def surgery_duration(self, name):

        if (self.surgery_end_date and self.surgery_date):
            return self.surgery_end_date - self.surgery_date
        else:
            return None

    def patient_age_at_surgery(self, name):
        if (self.patient.name.dob and self.surgery_date):
            rdelta = relativedelta(self.surgery_date.date(),
                                   self.patient.name.dob)
            years_months_days = str(rdelta.years) + 'y ' \
                + str(rdelta.months) + 'm ' \
                + str(rdelta.days) + 'd'
            return years_months_days
        else:
            return None

    patient = fields.Many2One('gnuhealth.patient', 'Patient', required=True)
    admission = fields.Many2One('gnuhealth.appointment', 'Admission')
    operating_room = fields.Many2One('gnuhealth.hospital.or', 'Operating Room')
    code = fields.Char('Code',
                       readonly=True,
                       help="Health Center code / sequence")

    procedures = fields.One2Many(
        'gnuhealth.operation',
        'name',
        'Procedures',
        help="List of the procedures in the surgery. Please enter the first "
        "one as the main procedure")

    supplies = fields.One2Many(
        'gnuhealth.surgery_supply',
        'name',
        'Supplies',
        help="List of the supplies required for the surgery")

    pathology = fields.Many2One('gnuhealth.pathology',
                                'Condition',
                                help="Base Condition / Reason")

    classification = fields.Selection([
        (None, ''),
        ('o', 'Optional'),
        ('r', 'Required'),
        ('u', 'Urgent'),
        ('e', 'Emergency'),
    ],
                                      'Urgency',
                                      help="Urgency level for this surgery",
                                      sort=False)
    surgeon = fields.Many2One('gnuhealth.healthprofessional',
                              'Surgeon',
                              help="Surgeon who did the procedure")

    anesthetist = fields.Many2One('gnuhealth.healthprofessional',
                                  'Anesthetist',
                                  help="Anesthetist in charge")

    surgery_date = fields.DateTime('Date', help="Start of the Surgery")

    surgery_end_date = fields.DateTime(
        'End',
        states={
            'required': Equal(Eval('state'), 'done'),
        },
        help="Automatically set when the surgery is done."
        "It is also the estimated end time when confirming the surgery.")

    surgery_length = fields.Function(
        fields.TimeDelta('Duration',
                         states={
                             'invisible':
                             And(Not(Equal(Eval('state'), 'done')),
                                 Not(Equal(Eval('state'), 'signed')))
                         },
                         help="Length of the surgery"), 'surgery_duration')

    state = fields.Selection([
        ('draft', 'Draft'),
        ('confirmed', 'Confirmed'),
        ('cancelled', 'Cancelled'),
        ('in_progress', 'In Progress'),
        ('done', 'Done'),
        ('signed', 'Signed'),
    ],
                             'State',
                             readonly=True,
                             sort=False)

    signed_by = fields.Many2One(
        'gnuhealth.healthprofessional',
        'Signed by',
        readonly=True,
        states={'invisible': Not(Equal(Eval('state'), 'signed'))},
        help="Health Professional that signed this surgery document")

    # age is deprecated in GNU Health 2.0
    age = fields.Char('Estimative Age',
                      help="Use this field for historical purposes, \
        when no date of surgery is given")

    computed_age = fields.Function(
        fields.Char('Age',
                    help="Computed patient age at the moment of the surgery"),
        'patient_age_at_surgery')

    gender = fields.Function(fields.Selection([
        (None, ''),
        ('m', 'Male'),
        ('f', 'Female'),
        ('f-m', 'Female -> Male'),
        ('m-f', 'Male -> Female'),
    ], 'Gender'),
                             'get_patient_gender',
                             searcher='search_patient_gender')

    description = fields.Char('Description')
    preop_mallampati = fields.Selection([
        (None, ''),
        ('Class 1', 'Class 1: Full visibility of tonsils, uvula and soft '
         'palate'),
        ('Class 2', 'Class 2: Visibility of hard and soft palate, '
         'upper portion of tonsils and uvula'),
        ('Class 3', 'Class 3: Soft and hard palate and base of the uvula are '
         'visible'),
        ('Class 4', 'Class 4: Only Hard Palate visible'),
    ],
                                        'Mallampati Score',
                                        sort=False)
    preop_bleeding_risk = fields.Boolean(
        'Risk of Massive bleeding',
        help="Patient has a risk of losing more than 500 "
        "ml in adults of over 7ml/kg in infants. If so, make sure that "
        "intravenous access and fluids are available")

    preop_oximeter = fields.Boolean('Pulse Oximeter in place',
                                    help="Pulse oximeter is in place "
                                    "and functioning")

    preop_site_marking = fields.Boolean(
        'Surgical Site Marking',
        help="The surgeon has marked the surgical incision")

    preop_antibiotics = fields.Boolean(
        'Antibiotic Prophylaxis',
        help="Prophylactic antibiotic treatment within the last 60 minutes")

    preop_sterility = fields.Boolean(
        'Sterility confirmed',
        help="Nursing team has confirmed sterility of the devices and room")

    preop_asa = fields.Selection([
        (None, ''),
        ('ps1', 'PS 1 : Normal healthy patient'),
        ('ps2', 'PS 2 : Patients with mild systemic disease'),
        ('ps3', 'PS 3 : Patients with severe systemic disease'),
        ('ps4', 'PS 4 : Patients with severe systemic disease that is'
         ' a constant threat to life '),
        ('ps5', 'PS 5 : Moribund patients who are not expected to'
         ' survive without the operation'),
        ('ps6', 'PS 6 : A declared brain-dead patient who organs are'
         ' being removed for donor purposes'),
    ],
                                 'ASA PS',
                                 help="ASA pre-operative Physical Status",
                                 sort=False)

    preop_rcri = fields.Many2One(
        'gnuhealth.rcri',
        'RCRI',
        help='Patient Revised Cardiac Risk Index\n'
        'Points 0: Class I Very Low (0.4% complications)\n'
        'Points 1: Class II Low (0.9% complications)\n'
        'Points 2: Class III Moderate (6.6% complications)\n'
        'Points 3 or more : Class IV High (>11% complications)')

    surgical_wound = fields.Selection([
        (None, ''),
        ('I', 'Clean . Class I'),
        ('II', 'Clean-Contaminated . Class II'),
        ('III', 'Contaminated . Class III'),
        ('IV', 'Dirty-Infected . Class IV'),
    ],
                                      'Surgical wound',
                                      sort=False)

    extra_info = fields.Text('Extra Info')

    anesthesia_report = fields.Text('Anesthesia Report')

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

    report_surgery_date = fields.Function(fields.Date('Surgery Date'),
                                          'get_report_surgery_date')
    report_surgery_time = fields.Function(fields.Time('Surgery Time'),
                                          'get_report_surgery_time')

    surgery_team = fields.One2Many(
        'gnuhealth.surgery_team',
        'name',
        'Team Members',
        help="Professionals Involved in the surgery")

    postoperative_dx = fields.Many2One(
        'gnuhealth.pathology',
        'Post-op dx',
        states={
            'invisible':
            And(Not(Equal(Eval('state'), 'done')),
                Not(Equal(Eval('state'), 'signed')))
        },
        help="Post-operative diagnosis")

    @staticmethod
    def default_institution():
        HealthInst = Pool().get('gnuhealth.institution')
        institution = HealthInst.get_institution()
        return institution

    @staticmethod
    def default_surgery_date():
        return datetime.now()

    @staticmethod
    def default_surgeon():
        pool = Pool()
        HealthProf = pool.get('gnuhealth.healthprofessional')
        surgeon = HealthProf.get_health_professional()
        return surgeon

    @staticmethod
    def default_state():
        return 'draft'

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

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

    # Show the gender and age upon entering the patient
    # These two are function fields (don't exist at DB level)
    @fields.depends('patient')
    def on_change_patient(self):
        gender = None
        age = ''
        self.gender = self.patient.gender
        self.computed_age = self.patient.age

    @classmethod
    def create(cls, vlist):
        Sequence = Pool().get('ir.sequence')
        Config = Pool().get('gnuhealth.sequences')

        vlist = [x.copy() for x in vlist]
        for values in vlist:
            if not values.get('code'):
                config = Config(1)
                values['code'] = Sequence.get_id(
                    config.surgery_code_sequence.id)
        return super(Surgery, cls).create(vlist)

    @classmethod
    # Update to version 2.0
    def __register__(cls, module_name):
        cursor = Transaction().cursor
        TableHandler = backend.get('TableHandler')
        table = TableHandler(cursor, cls, module_name)
        # Rename the date column to surgery_surgery_date
        if table.column_exist('date'):
            table.column_rename('date', 'surgery_date')

        super(Surgery, cls).__register__(module_name)

    @classmethod
    def __setup__(cls):
        super(Surgery, cls).__setup__()
        cls._error_messages.update({
            'end_date_before_start':
            'End time "%(end_date)s" BEFORE '
            'surgery date "%(surgery_date)s"',
            'or_is_not_available':
            'Operating Room is not available'
        })

        cls._buttons.update({
            'confirmed': {
                'invisible':
                And(Not(Equal(Eval('state'), 'draft')),
                    Not(Equal(Eval('state'), 'cancelled'))),
            },
            'cancel': {
                'invisible': Not(Equal(Eval('state'), 'confirmed')),
            },
            'start': {
                'invisible': Not(Equal(Eval('state'), 'confirmed')),
            },
            'done': {
                'invisible': Not(Equal(Eval('state'), 'in_progress')),
            },
            'signsurgery': {
                'invisible': Not(Equal(Eval('state'), 'done')),
            },
        })

    @classmethod
    def validate(cls, surgeries):
        super(Surgery, cls).validate(surgeries)
        for surgery in surgeries:
            surgery.validate_surgery_period()

    def validate_surgery_period(self):
        Lang = Pool().get('ir.lang')

        language, = Lang.search([
            ('code', '=', Transaction().language),
        ])
        if (self.surgery_end_date and self.surgery_date):
            if (self.surgery_end_date < self.surgery_date):
                self.raise_user_error(
                    'end_date_before_start', {
                        'surgery_date':
                        Lang.strftime(self.surgery_date, language.code,
                                      language.date),
                        'end_date':
                        Lang.strftime(self.surgery_end_date, language.code,
                                      language.date),
                    })

    @classmethod
    def write(cls, surgeries, vals):
        # Don't allow to write the record if the surgery has been signed
        if surgeries[0].state == 'signed':
            cls.raise_user_error(
                "This surgery is at state Done and has been signed\n"
                "You can no longer modify it.")
        return super(Surgery, cls).write(surgeries, vals)

    ## Method to check for availability and make the Operating Room reservation
    # for the associated surgery

    @classmethod
    @ModelView.button
    def confirmed(cls, surgeries):
        surgery_id = surgeries[0]
        Operating_room = Pool().get('gnuhealth.hospital.or')
        cursor = Transaction().cursor

        # Operating Room and end surgery time check
        if (not surgery_id.operating_room or not surgery_id.surgery_end_date):
            cls.raise_user_error("Operating Room and estimated end time  "
                                 "are needed in order to confirm the surgery")

        or_id = surgery_id.operating_room.id
        cursor.execute(
            "SELECT COUNT(*) \
            FROM gnuhealth_surgery \
            WHERE (surgery_date::timestamp,surgery_end_date::timestamp) \
                OVERLAPS (timestamp %s, timestamp %s) \
              AND (state = %s or state = %s) \
              AND operating_room = CAST(%s AS INTEGER) ",
            (surgery_id.surgery_date, surgery_id.surgery_end_date, 'confirmed',
             'in_progress', str(or_id)))
        res = cursor.fetchone()
        if (surgery_id.surgery_end_date < surgery_id.surgery_date):
            cls.raise_user_error("The Surgery end date must later than the \
                Start")
        if res[0] > 0:
            cls.raise_user_error('or_is_not_available')
        else:
            cls.write(surgeries, {'state': 'confirmed'})

    # Cancel the surgery and set it to draft state
    # Free the related Operating Room

    @classmethod
    @ModelView.button
    def cancel(cls, surgeries):
        surgery_id = surgeries[0]
        Operating_room = Pool().get('gnuhealth.hospital.or')

        cls.write(surgeries, {'state': 'cancelled'})

    # Start the surgery

    @classmethod
    @ModelView.button
    def start(cls, surgeries):
        surgery_id = surgeries[0]
        Operating_room = Pool().get('gnuhealth.hospital.or')

        cls.write(
            surgeries, {
                'state': 'in_progress',
                'surgery_date': datetime.now(),
                'surgery_end_date': datetime.now()
            })
        Operating_room.write([surgery_id.operating_room],
                             {'state': 'occupied'})

    # Finnish the surgery
    # Free the related Operating Room

    @classmethod
    @ModelView.button
    def done(cls, surgeries):
        surgery_id = surgeries[0]
        Operating_room = Pool().get('gnuhealth.hospital.or')

        cls.write(surgeries, {
            'state': 'done',
            'surgery_end_date': datetime.now()
        })

        Operating_room.write([surgery_id.operating_room], {'state': 'free'})

    # Sign the surgery document, and the surgical act.

    @classmethod
    @ModelView.button
    def signsurgery(cls, surgeries):
        surgery_id = surgeries[0]

        # Sign, change the state of the Surgery to "Signed"
        # and write the name of the signing health professional

        signing_hp = Pool().get(
            'gnuhealth.healthprofessional').get_health_professional()
        if not signing_hp:
            cls.raise_user_error(
                "No health professional associated to this user !")

        cls.write(surgeries, {'state': 'signed', 'signed_by': signing_hp})

    def get_report_surgery_date(self, name):
        Company = Pool().get('company.company')

        timezone = None
        company_id = Transaction().context.get('company')
        if company_id:
            company = Company(company_id)
            if company.timezone:
                timezone = pytz.timezone(company.timezone)

        dt = self.surgery_date
        return datetime.astimezone(dt.replace(tzinfo=pytz.utc),
                                   timezone).date()

    def get_report_surgery_time(self, name):
        Company = Pool().get('company.company')

        timezone = None
        company_id = Transaction().context.get('company')
        if company_id:
            company = Company(company_id)
            if company.timezone:
                timezone = pytz.timezone(company.timezone)

        dt = self.surgery_date
        return datetime.astimezone(dt.replace(tzinfo=pytz.utc),
                                   timezone).time()
Esempio n. 16
0
class CreatePurchaseAskParty(ModelView):
    'Create Purchase Ask Party'
    __name__ = 'purchase.request.create_purchase.ask_party'
    product = fields.Many2One('product.product', 'Product', readonly=True)
    company = fields.Many2One('company.company', 'Company', readonly=True)
    party = fields.Many2One('party.party', 'Supplier', required=True)
Esempio n. 17
0
class RCRI(ModelSQL, ModelView):
    'Revised Cardiac Risk Index'
    __name__ = 'gnuhealth.rcri'

    patient = fields.Many2One('gnuhealth.patient', 'Patient ID', required=True)
    rcri_date = fields.DateTime('Date', required=True)
    health_professional = fields.Many2One(
        'gnuhealth.healthprofessional',
        'Health Professional',
        help="Health professional /"
        "Cardiologist who signed the assesment RCRI")

    rcri_high_risk_surgery = fields.Boolean(
        'High Risk surgery',
        help='Includes andy suprainguinal vascular, intraperitoneal,'
        ' or intrathoracic procedures')

    rcri_ischemic_history = fields.Boolean(
        'History of ischemic heart disease',
        help="history of MI or a positive exercise test, current \
        complaint of chest pain considered to be secondary to myocardial \
        ischemia, use of nitrate therapy, or ECG with pathological \
        Q waves; do not count prior coronary revascularization procedure \
        unless one of the other criteria for ischemic heart disease is \
        present")

    rcri_congestive_history = fields.Boolean(
        'History of congestive heart disease')

    rcri_diabetes_history = fields.Boolean(
        'Preoperative Diabetes',
        help="Diabetes Mellitus requiring treatment with Insulin")

    rcri_cerebrovascular_history = fields.Boolean(
        'History of Cerebrovascular disease')

    rcri_kidney_history = fields.Boolean(
        'Preoperative Kidney disease',
        help="Preoperative serum creatinine >2.0 mg/dL (177 mol/L)")

    rcri_total = fields.Integer(
        'Score',
        help='Points 0: Class I Very Low (0.4% complications)\n'
        'Points 1: Class II Low (0.9% complications)\n'
        'Points 2: Class III Moderate (6.6% complications)\n'
        'Points 3 or more : Class IV High (>11% complications)')

    rcri_class = fields.Selection([
        (None, ''),
        ('I', 'I'),
        ('II', 'II'),
        ('III', 'III'),
        ('IV', 'IV'),
    ],
                                  'RCRI Class',
                                  sort=False)

    @fields.depends('rcri_high_risk_surgery', 'rcri_ischemic_history',
                    'rcri_congestive_history', 'rcri_diabetes_history',
                    'rcri_cerebrovascular_history', 'rcri_kidney_history')
    def on_change_with_rcri_total(self):

        total = 0
        if self.rcri_high_risk_surgery:
            total = total + 1
        if self.rcri_ischemic_history:
            total = total + 1
        if self.rcri_congestive_history:
            total = total + 1
        if self.rcri_diabetes_history:
            total = total + 1
        if self.rcri_kidney_history:
            total = total + 1
        if self.rcri_cerebrovascular_history:
            total = total + 1

        return total

    @fields.depends('rcri_high_risk_surgery', 'rcri_ischemic_history',
                    'rcri_congestive_history', 'rcri_diabetes_history',
                    'rcri_cerebrovascular_history', 'rcri_kidney_history')
    def on_change_with_rcri_class(self):
        rcri_class = ''

        total = 0
        if self.rcri_high_risk_surgery:
            total = total + 1
        if self.rcri_ischemic_history:
            total = total + 1
        if self.rcri_congestive_history:
            total = total + 1
        if self.rcri_diabetes_history:
            total = total + 1
        if self.rcri_kidney_history:
            total = total + 1
        if self.rcri_cerebrovascular_history:
            total = total + 1

        if total == 0:
            rcri_class = 'I'
        if total == 1:
            rcri_class = 'II'
        if total == 2:
            rcri_class = 'III'
        if (total > 2):
            rcri_class = 'IV'

        return rcri_class

    @staticmethod
    def default_rcri_date():
        return datetime.now()

    @staticmethod
    def default_rcri_total():
        return 0

    @staticmethod
    def default_rcri_class():
        return 'I'

    def get_rec_name(self, name):
        res = 'Points: ' + str(self.rcri_total) + ' (Class ' + \
            str(self.rcri_class) + ')'
        return res
Esempio n. 18
0
class PriceData(ModelSQL, ModelView):
    """Pricedata"""
    __name__ = "price_master_datas.pricedata"

    product_name = fields.Char('product_name', select=True)  # 产品名称
    product_code = fields.Char('product_code', select=True)  # 药品编码
    retrieve_the_code = fields.Many2One('product.product',
                                        'retrieve_the_code',
                                        required=True,
                                        select=True)
    code = fields.Function(
        fields.Char("Code",
                    size=None,
                    select=True,
                    states=STATES,
                    depends=DEPENDS), 'get_code', 'set_code')
    name = fields.Function(
        fields.Char("Name",
                    size=None,
                    required=False,
                    translate=True,
                    select=True), "get_name", "set_name")
    attach = fields.Char('attach', select=True)
    drug_specifications = fields.Char('drug_specifications',
                                      select=True,
                                      required=False)
    cost_price = fields.Numeric('cost_price', select=True, digits=price_digits)
    list_price = fields.Numeric('list_price', select=True, digits=price_digits)
    new_cost_price = fields.Float('new_cost_price',
                                  select=True,
                                  required=True,
                                  digits=price_digits)
    new_list_price = fields.Float('new_list_price',
                                  select=True,
                                  required=True,
                                  digits=price_digits)
    modify_reason = fields.Selection([
        ('00', u'来药单'),
        ('01', u'海虹药通'),
    ],
                                     'Modify_reason',
                                     select=True)
    effective_date = fields.Date('effective_date', select=True, required=True)

    price_digits = (16, config.getint('product', '', default=4))  # 小数点保留问题

    @classmethod
    def delete(cls, records):
        return cls.raise_user_error(u'历史单据不允许删除')  # 错误信息弹框提示

    @classmethod
    def write(cls, records, values, *args):
        return cls.raise_user_error(u'历史单据不允许修改')

    @classmethod
    def create(cls, vlist):
        UserId = Pool().get('hrp_internal_delivery.test_straight')
        location_id = UserId.get_warehouse_frozen_id()
        # Config = Pool().get('purchase.configuration')
        # config = Config(1)  # 库存地配置
        # location_id = [config.hospital.id,config.outpatient_service.id,config.warehouse.id,config.medical.id,config.endoscopic.id,config.preparation.id,config.ward.id,config.herbs.id]
        price_content = Pool().get('hrp_report.price_profit_loss_content')
        UomCategory = Pool().get('product.category')
        Date = Pool().get('ir.date')
        today = str(Date.today())
        price = Pool().get("product.template")
        product = Pool().get("product.product")
        if str(vlist[0]['effective_date']) == today:
            product_id = vlist[0]['retrieve_the_code']
            Product = product.search([('id', '=', product_id)])
            product_template_id = Product[0].template.id
            New = price.search([('id', '=', product_template_id)])
            if New:
                lvc = {
                    'list_price': vlist[0]['new_list_price'],
                    'cost_price': vlist[0]['new_cost_price']
                }
                price.write(New, lvc)
                content = []
                for each in location_id:
                    content_dict = {}
                    with Transaction().set_context(
                            stock_date_end=Date.today()):
                        quantities = product.products_by_location(
                            [each['warehouse']], [product_id],
                            with_childs=True)
                        if quantities.values():
                            stock_level_warehouse = [
                                v for v in quantities.values()
                            ][0]
                        else:
                            stock_level_warehouse = 0
                    with Transaction().set_context(
                            stock_date_end=Date.today()):
                        quantities = product.products_by_location(
                            [each['freeze']], [product_id], with_childs=True)
                        if quantities.values():
                            stock_level_freeze = [
                                v for v in quantities.values()
                            ][0]
                        else:
                            stock_level_freeze = 0
                        stock_level = stock_level_warehouse + stock_level_freeze
                        party = Product[0].product_suppliers
                        if party:
                            party = party[0].party.name
                        else:
                            party = ''
                        categories = [i.id for i in Product[0].categories]
                        uom_category = UomCategory.search([('id', '=',
                                                            categories[0])])
                        uom_name = uom_category[0].name
                        if uom_name == u'西药':
                            content_dict['drug_type'] = '00'
                        if uom_name == u'中成药':
                            content_dict['drug_type'] = '01'
                        if uom_name == u'中草药':
                            content_dict['drug_type'] = '02'
                        if uom_name == u'颗粒中':
                            content_dict['drug_type'] = '03'
                        if uom_name == u'原料药':
                            content_dict['drug_type'] = '04'
                        if uom_name == u'敷药':
                            content_dict['drug_type'] = '05'
                        if uom_name == u'同位素':
                            content_dict['drug_type'] = '07'
                        content_dict['location'] = each['warehouse']
                        content_dict['code'] = vlist[0]['product_code']
                        content_dict['product'] = vlist[0]['product_name']
                        content_dict['drug_specifications'] = vlist[0][
                            'drug_specifications']
                        content_dict['list_price'] = vlist[0]['list_price']
                        content_dict['cost_price'] = vlist[0]['cost_price']
                        content_dict['new_list_price'] = vlist[0][
                            'new_list_price']
                        content_dict['new_cost_price'] = vlist[0][
                            'new_cost_price']
                        content_dict['inventory'] = float(stock_level)
                        content_dict['effective_date'] = today
                        content_dict['party'] = party
                        content_dict['uom'] = Product[
                            0].template.default_uom.id
                        content_dict['price_profit_loss'] = decimal.Decimal(
                            str(stock_level)) * (decimal.Decimal(
                                str(vlist[0]['new_cost_price'])) -
                                                 vlist[0]['cost_price'])
                        content_dict[
                            'price_list_profit_loss'] = decimal.Decimal(
                                str(stock_level)) * (decimal.Decimal(
                                    str(vlist[0]['new_list_price'])) -
                                                     vlist[0]['list_price'])
                        content.append(content_dict)
                price_content.create(content)
            else:
                pass
        return super(PriceData, cls).create(vlist)

    @classmethod
    def create_profit_loss(cls, Pricedata):
        for price_data in Pricedata:
            UserId = Pool().get('hrp_internal_delivery.test_straight')
            location_id = UserId.get_warehouse_frozen_id()
            price_content = Pool().get('hrp_report.price_profit_loss_content')
            UomCategory = Pool().get('product.category')
            Date = Pool().get('ir.date')
            today = str(Date.today())
            product = Pool().get('product.product')
            content = []
            for each in location_id:
                dict = {}
                with Transaction().set_context(stock_date_end=Date.today()):
                    quantities = product.products_by_location(
                        [each['warehouse']], [price_data.retrieve_the_code.id],
                        with_childs=True)
                    if quantities.values():
                        stock_level_warehouse = [
                            v for v in quantities.values()
                        ][0]
                    else:
                        stock_level_warehouse = 0
                with Transaction().set_context(stock_date_end=Date.today()):
                    quantities = product.products_by_location(
                        [each['freeze']], [price_data.retrieve_the_code.id],
                        with_childs=True)
                    if quantities.values():
                        stock_level_freeze = [v
                                              for v in quantities.values()][0]
                    else:
                        stock_level_freeze = 0
                    stock_level = stock_level_warehouse + stock_level_freeze
                    party = price_data.retrieve_the_code.product_suppliers
                    if party:
                        party = party[0].party.name
                    else:
                        party = ''
                    categories = [
                        i.id for i in price_data.retrieve_the_code.categories
                    ]
                    uom_category = UomCategory.search([('id', '=',
                                                        categories[0])])
                    uom_name = uom_category[0].name
                    if uom_name == u'西药':
                        dict['drug_type'] = '00'
                    if uom_name == u'中成药':
                        dict['drug_type'] = '01'
                    if uom_name == u'中草药':
                        dict['drug_type'] = '02'
                    if uom_name == u'颗粒中':
                        dict['drug_type'] = '03'
                    if uom_name == u'原料药':
                        dict['drug_type'] = '04'
                    if uom_name == u'敷药':
                        dict['drug_type'] = '05'
                    if uom_name == u'同位素':
                        dict['drug_type'] = '07'
                    dict['location'] = each['warehouse']
                    dict['code'] = price_data.product_code
                    dict['product'] = price_data.product_name
                    dict[
                        'drug_specifications'] = price_data.drug_specifications
                    dict['list_price'] = price_data.list_price
                    dict['cost_price'] = price_data.cost_price
                    dict['new_list_price'] = price_data.new_list_price
                    dict['new_cost_price'] = price_data.new_cost_price
                    dict['inventory'] = float(stock_level)
                    dict['effective_date'] = today
                    dict['party'] = party
                    dict[
                        'uom'] = price_data.retrieve_the_code.template.default_uom.id
                    dict['price_profit_loss'] = decimal.Decimal(
                        str(stock_level)) * (
                            decimal.Decimal(str(price_data.new_cost_price)) -
                            price_data.cost_price)
                    dict['price_list_profit_loss'] = decimal.Decimal(
                        str(stock_level)) * (
                            decimal.Decimal(str(price_data.new_list_price)) -
                            price_data.list_price)
                    content.append(dict)
            price_content.create(content)

    @fields.depends('retrieve_the_code')
    def on_change_retrieve_the_code(self):
        if self.retrieve_the_code != '':
            try:
                Retrieve = self.retrieve_the_code.name
                Attach = self.retrieve_the_code.attach
                Code = self.retrieve_the_code.code
                Drug_specifications = self.retrieve_the_code.drug_specifications
                Cost_price = self.retrieve_the_code.cost_price
                List_price = self.retrieve_the_code.list_price
                hrp = Pool().get('product.product')
                HRP = hrp.search([('id', '=', self.retrieve_the_code.id)])
                if HRP:
                    try:
                        self.product_name = Retrieve
                        self.product_code = Code
                        self.name = Retrieve
                        self.attach = Attach
                        self.code = Code
                        self.drug_specifications = str(Drug_specifications)
                        self.cost_price = Cost_price
                        self.list_price = List_price
                    except:
                        pass
            except:
                pass

    def get_code(self, name):
        pool = Pool()
        try:
            product_templates = pool.get('product.product')
            product_template = product_templates.search([
                ("id", "=", int(self.retrieve_the_code.mark.id))
            ])
            code = product_template[0].code
        except:
            return None
        return code

    def get_name(self, name):
        pool = Pool()
        try:
            product_templates = pool.get('product.template')
            product_template = product_templates.search([
                ("id", "=", int(self.retrieve_the_code.mark.id))
            ])
            name = product_template[0].name
        except:
            return None
        return name

    @classmethod
    def set_code(cls, set_code, name, value):
        pass

    @classmethod
    def set_name(cls, set_name, name, value):
        pass

    @classmethod
    def write_price(cls):
        Price = Pool().get('price_master_datas.pricedata')
        with Transaction().set_context(user=1):
            Date = Pool().get('ir.date')
            today = str(Date.today())
            pricedata = Pool().get('price_master_datas.pricedata')
            Pricedata = pricedata.search([('effective_date', '=', today)])
            price = Pool().get("product.template")
            if Pricedata:
                for i in Pricedata:
                    Retri = i.retrieve_the_code.template.id
                    New_cost_price = i.new_cost_price
                    New_list_price = i.new_list_price
                    New = price.search([('id', '=', Retri)])
                    if New:
                        lvc = {
                            'list_price': New_list_price,
                            'cost_price': New_cost_price
                        }
                        price.write(New, lvc)

                    else:
                        pass
                Price.create_profit_loss(Pricedata)
Esempio n. 19
0
class RenumerationPurposeandPay(ModelSQL, ModelView):
    '''Renumeration Purpose and Pay'''

    __name__ = 'exam_type.purpose_and_pay_renum'

    renumeration = fields.Many2One('exam_section.renumeration_bill',
                                   'Renumeration Bill')
    exam_type = fields.Function(
        fields.Many2One('exam_section.exam_type', 'Exam Type'),
        'on_change_with_exam_type')
    external = fields.Function(fields.Boolean('External'),
                               'on_change_with_external')
    payment_basis = fields.Char('Payment Basis')
    unit = fields.Integer('Unit')
    purpose = fields.Many2One('exam_section.exam_type.renumeration',
                              'Purpose',
                              domain=[('exam_type', '=', Eval('exam_type')),
                                      ('is_external', '=', Eval('external'))],
                              depends=['exam_type', 'external'])
    amount = fields.Function(fields.Float('Amount'),
                             'calculate_amount_payable')

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._error_messages.update({
            'amt_lt_0': 'Amount is less than zero',
            'max_range': 'Maximum Amount exceeded',
        })

    @classmethod
    def validate(cls, records):
        super(RenumerationPurposeandPay, cls).validate(records)
        for record in records:
            record.check_amount()

    def check_amount(self):
        '''Check whether: 
                (i) amount is less thanm zero or not
                (ii) amount exceeds maximum amount 
                     allowed for spefific purpose or not'''
        amount = self.amount
        purpose = self.purpose
        if amount and purpose:
            if amount < 0:
                self.raise_user_error('amt_lt_0')
            elif purpose.max_range != 0 and amount > purpose.max_range:
                self.raise_user_error('max_range')

    # @classmethod
    # def show_exam_type(cls, records):
    #     for rec in records:
    #         print(rec.renumeration.exam.exam_type)

    @fields.depends('renumeration')
    def on_change_with_external(self, name=None):
        '''Function for hidden field to determine whether bill 
           is for AIIMS Employee or external examiner'''
        if self.renumeration:
            if self.renumeration.type_of_examiner == 'external':
                return True
        return False

    @fields.depends('renumeration')
    def on_change_with_exam_type(self, name=None):
        '''Function for hidden field to fetch type of examination'''
        if self.renumeration and self.renumeration.exam:
            return self.renumeration.exam.exam_type.id

    @fields.depends('purpose')
    def on_change_with_payment_basis(self):
        '''Function for fetching payment basis
           (Daily, Hourly, Per Session, Per Copy, Per Exam)'''
        if self.purpose and self.purpose.payment_basis:
            return self.purpose.payment_basis.name

    @fields.depends('purpose', 'renumeration')
    def calculate_amount_payable(self, name):
        '''Calculate total amount for Renumeration Bill'''
        res = 0
        if self.purpose and self.unit:
            res = self.unit * self.purpose.type_amount_fix
            if self.purpose.min_range and res < self.purpose.min_range:
                res = self.purpose.min_range
        return res
Esempio n. 20
0
class FiscalYear(Workflow, ModelSQL, ModelView):
    'Fiscal Year'
    __name__ = 'account.fiscalyear'
    name = fields.Char('Name', size=None, required=True, depends=DEPENDS)
    start_date = fields.Date('Starting Date',
                             required=True,
                             states=STATES,
                             domain=[('start_date', '<=',
                                      Eval('end_date', None))],
                             depends=DEPENDS + ['end_date'])
    end_date = fields.Date('Ending Date',
                           required=True,
                           states=STATES,
                           domain=[('end_date', '>=', Eval('start_date',
                                                           None))],
                           depends=DEPENDS + ['start_date'])
    periods = fields.One2Many('account.period',
                              'fiscalyear',
                              'Periods',
                              states=STATES,
                              depends=DEPENDS)
    state = fields.Selection([
        ('open', 'Open'),
        ('close', 'Close'),
        ('locked', 'Locked'),
    ],
                             'State',
                             readonly=True,
                             required=True)
    post_move_sequence = fields.Many2One(
        'ir.sequence',
        'Post Move Sequence',
        required=True,
        domain=[
            ('code', '=', 'account.move'),
            ['OR', ('company', '=', Eval('company')), ('company', '=', None)]
        ],
        context={
            'code': 'account.move',
            'company': Eval('company'),
        },
        depends=['company'])
    company = fields.Many2One(
        'company.company',
        'Company',
        required=True,
        domain=[
            ('id', If(Eval('context', {}).contains('company'), '=',
                      '!='), Eval('context', {}).get('company', -1)),
        ],
        select=True)
    icon = fields.Function(fields.Char("Icon"), 'get_icon')

    @classmethod
    def __setup__(cls):
        super(FiscalYear, cls).__setup__()
        cls._order.insert(0, ('start_date', 'ASC'))
        cls._error_messages.update({
            'change_post_move_sequence':
            ('You can not change the post '
             'move sequence in fiscal year "%s".'),
            'no_fiscalyear_date':
            'No fiscal year defined for "%s".',
            'fiscalyear_overlaps': ('Fiscal year "%(first)s" and '
                                    '"%(second)s" overlap.'),
            'different_post_move_sequence':
            ('Fiscal year "%(first)s" and '
             '"%(second)s" have the same post move sequence.'),
            'account_balance_not_zero': ('The balance of the account "%s" '
                                         'must be zero.'),
            'close_error': ('You can not close fiscal year "%s" until you '
                            'close all previous fiscal years.'),
            'reopen_error': ('You can not reopen fiscal year "%s" until '
                             'you reopen all later fiscal years.'),
        })
        cls._transitions |= set((
            ('open', 'close'),
            ('close', 'locked'),
            ('close', 'open'),
        ))
        cls._buttons.update({
            'create_period': {
                'invisible': ((Eval('state') != 'open')
                              | Eval('periods', [0])),
                'depends': ['state'],
            },
            'create_period_3': {
                'invisible': ((Eval('state') != 'open')
                              | Eval('periods', [0])),
                'depends': ['state'],
            },
            'close': {
                'invisible': Eval('state') != 'open',
                'depends': ['state'],
            },
            'reopen': {
                'invisible': Eval('state') != 'close',
                'depends': ['state'],
            },
            'lock': {
                'invisible': Eval('state') != 'close',
                'depends': ['state'],
            },
        })

    @staticmethod
    def default_state():
        return 'open'

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

    def get_icon(self, name):
        return {
            'open': 'tryton-open',
            'close': 'tryton-close',
            'locked': 'tryton-readonly',
        }.get(self.state)

    @classmethod
    def validate(cls, years):
        super(FiscalYear, cls).validate(years)
        for year in years:
            year.check_dates()
            year.check_post_move_sequence()

    def check_dates(self):
        transaction = Transaction()
        connection = transaction.connection
        transaction.database.lock(connection, self._table)
        cursor = connection.cursor()
        table = self.__table__()
        cursor.execute(
            *table.select(table.id,
                          where=(((table.start_date <= self.start_date)
                                  & (table.end_date >= self.start_date))
                                 | ((table.start_date <= self.end_date)
                                    & (table.end_date >= self.end_date))
                                 | ((table.start_date >= self.start_date)
                                    & (table.end_date <= self.end_date)))
                          & (table.company == self.company.id)
                          & (table.id != self.id)))
        second_id = cursor.fetchone()
        if second_id:
            second = self.__class__(second_id[0])
            self.raise_user_error('fiscalyear_overlaps', {
                'first': self.rec_name,
                'second': second.rec_name,
            })

    def check_post_move_sequence(self):
        years = self.search([
            ('post_move_sequence', '=', self.post_move_sequence.id),
            ('id', '!=', self.id),
        ])
        if years:
            self.raise_user_error('different_post_move_sequence', {
                'first': self.rec_name,
                'second': years[0].rec_name,
            })

    @classmethod
    def write(cls, *args):
        actions = iter(args)
        for fiscalyears, values in zip(actions, actions):
            if values.get('post_move_sequence'):
                for fiscalyear in fiscalyears:
                    if (fiscalyear.post_move_sequence
                            and fiscalyear.post_move_sequence.id !=
                            values['post_move_sequence']):
                        cls.raise_user_error('change_post_move_sequence',
                                             (fiscalyear.rec_name, ))
        super(FiscalYear, cls).write(*args)

    @classmethod
    def delete(cls, fiscalyears):
        Period = Pool().get('account.period')
        Period.delete([p for f in fiscalyears for p in f.periods])
        super(FiscalYear, cls).delete(fiscalyears)

    @classmethod
    @ModelView.button
    def create_period(cls, fiscalyears, interval=1):
        '''
        Create periods for the fiscal years with month interval
        '''
        Period = Pool().get('account.period')
        to_create = []
        for fiscalyear in fiscalyears:
            period_start_date = fiscalyear.start_date
            while period_start_date < fiscalyear.end_date:
                period_end_date = period_start_date + \
                    relativedelta(months=interval - 1) + \
                    relativedelta(day=31)
                if period_end_date > fiscalyear.end_date:
                    period_end_date = fiscalyear.end_date
                name = datetime_strftime(period_start_date, '%Y-%m')
                if name != datetime_strftime(period_end_date, '%Y-%m'):
                    name += ' - ' + datetime_strftime(period_end_date, '%Y-%m')
                to_create.append({
                    'name': name,
                    'start_date': period_start_date,
                    'end_date': period_end_date,
                    'fiscalyear': fiscalyear.id,
                    'type': 'standard',
                })
                period_start_date = period_end_date + relativedelta(days=1)
        if to_create:
            Period.create(to_create)

    @classmethod
    @ModelView.button
    def create_period_3(cls, fiscalyears):
        '''
        Create periods for the fiscal years with 3 months interval
        '''
        cls.create_period(fiscalyears, interval=3)

    @classmethod
    def find(cls, company_id, date=None, exception=True):
        '''
        Return the fiscal year for the company_id
            at the date or the current date.
        If exception is set the function will raise an exception
            if any fiscal year is found.
        '''
        pool = Pool()
        Lang = pool.get('ir.lang')
        Date = pool.get('ir.date')

        if not date:
            date = Date.today()
        fiscalyears = cls.search([
            ('start_date', '<=', date),
            ('end_date', '>=', date),
            ('company', '=', company_id),
        ],
                                 order=[('start_date', 'DESC')],
                                 limit=1)
        if not fiscalyears:
            if exception:
                lang = Lang.get()
                cls.raise_user_error('no_fiscalyear_date', lang.strftime(date))
            else:
                return None
        return fiscalyears[0].id

    def get_deferral(self, account):
        'Computes deferrals for accounts'
        pool = Pool()
        Currency = pool.get('currency.currency')
        Deferral = pool.get('account.account.deferral')

        if account.kind == 'view':
            return
        if not account.deferral:
            if not Currency.is_zero(self.company.currency, account.balance):
                self.raise_user_error('account_balance_not_zero',
                                      error_args=(account.rec_name, ))
        else:
            deferral = Deferral()
            deferral.account = account
            deferral.fiscalyear = self
            deferral.debit = account.debit
            deferral.credit = account.credit
            deferral.amount_second_currency = account.amount_second_currency
            return deferral

    @classmethod
    @ModelView.button
    @Workflow.transition('close')
    def close(cls, fiscalyears):
        '''
        Close a fiscal year
        '''
        pool = Pool()
        Period = pool.get('account.period')
        Account = pool.get('account.account')
        Deferral = pool.get('account.account.deferral')
        transaction = Transaction()
        database = transaction.database
        connection = transaction.connection

        # Lock period to be sure no new period will be created in between.
        database.lock(connection, Period._table)

        deferrals = []
        for fiscalyear in fiscalyears:
            if cls.search([
                ('end_date', '<=', fiscalyear.start_date),
                ('state', '=', 'open'),
                ('company', '=', fiscalyear.company.id),
            ]):
                cls.raise_user_error('close_error', (fiscalyear.rec_name, ))

            periods = Period.search([
                ('fiscalyear', '=', fiscalyear.id),
            ])
            Period.close(periods)

            with Transaction().set_context(fiscalyear=fiscalyear.id,
                                           date=None,
                                           cumulate=True):
                accounts = Account.search([
                    ('company', '=', fiscalyear.company.id),
                ])
                deferrals += [
                    _f for _f in (fiscalyear.get_deferral(a) for a in accounts)
                    if _f
                ]
        Deferral.save(deferrals)

    @classmethod
    @ModelView.button
    @Workflow.transition('open')
    def reopen(cls, fiscalyears):
        '''
        Re-open a fiscal year
        '''
        Deferral = Pool().get('account.account.deferral')

        for fiscalyear in fiscalyears:
            if cls.search([
                ('start_date', '>=', fiscalyear.end_date),
                ('state', '!=', 'open'),
                ('company', '=', fiscalyear.company.id),
            ]):
                cls.raise_user_error('reopen_error')

            deferrals = Deferral.search([
                ('fiscalyear', '=', fiscalyear.id),
            ])
            Deferral.delete(deferrals)

    @classmethod
    @ModelView.button
    @Workflow.transition('locked')
    def lock(cls, fiscalyears):
        pool = Pool()
        Period = pool.get('account.period')
        periods = Period.search([
            ('fiscalyear', 'in', [f.id for f in fiscalyears]),
        ])
        Period.lock(periods)
Esempio n. 21
0
class TADAHotelFood(ModelSQL, ModelView):
    '''TA/DA Hotel/Food'''

    __name__ = 'exam_section.ta_da.hotel_food'

    ta_da = fields.Many2One('exam_section.ta_da_bill', 'TA/DA Bill')
    type_ = fields.Selection([('hotel', 'Hotel'), ('food', 'Food')],
                             'Type',
                             required=True)
    no_of_nights_stayed = fields.Integer('No. of Nights Stayed',
                                         states={
                                             'readonly':
                                             ~Eval('type_').in_(['hotel']),
                                             'invisible':
                                             ~Eval('type_').in_(['hotel']),
                                         },
                                         depends=['type_'])
    no_of_days_food = fields.Integer('No. of Days Food',
                                     states={
                                         'readonly':
                                         ~Eval('type_').in_(['food']),
                                         'invisible':
                                         ~Eval('type_').in_(['food']),
                                     },
                                     depends=['type_'])
    from_date = fields.Date('From Date', required=True)
    to_date = fields.Date('To Date', required=True)
    amount = fields.Float('Amount', required=True)
    have_bill = fields.Boolean('Bill?')
    bill = fields.Many2One('ir.attachment',
                           'Bill',
                           states={
                               'invisible': Eval('have_bill') != '1',
                               'required': Eval('have_bill') == '1',
                           },
                           depends=['have_bill'])

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._error_messages.update({
            'date_error':
            'From date is more than to date',
            'amount_lt_0':
            'Amount cannot be less than 0'
        })

    @staticmethod
    def default_amount():
        return 0

    @fields.depends('from_date', 'to_date', 'type_')
    def on_change_with_no_of_nights_stayed(self):
        '''Calculate number of nights stayed in hotel using from_date
           and to_date'''
        if self.type_ == 'hotel':
            if self.from_date:
                if self.to_date:
                    no_of_days_delta = self.to_date - self.from_date
                    return int(no_of_days_delta.days)
        return 0

    @fields.depends('from_date', 'to_date', 'type_')
    def on_change_with_no_of_days_food(self):
        '''Calculate number of days of food consumption using from_date
           and to_date'''
        if self.type_ == 'food':
            if self.from_date:
                if self.to_date:
                    no_of_days_delta = self.to_date - self.from_date
                    return int(no_of_days_delta.days)
        return 0

    @classmethod
    def validate(cls, records):
        super(TADAHotelFood, cls).validate(records)
        for record in records:
            record.check_date()
            record.check_amount()

    def check_date(self):
        '''Check whether from_date is greater than to_date or not'''
        if self.from_date > self.to_date:
            self.raise_user_error('date_error')

    def check_amount(self):
        '''Check whether amount is less than zero or not'''
        if self.amount < 0:
            self.raise_user_error('amount_lt_0')
Esempio n. 22
0
class ModelViewChangedValuesTarget(ModelView):
    'ModelView Changed Values Target'
    __name__ = 'test.modelview.changed_values.target'
    name = fields.Char('Name')
    parent = fields.Many2One('test.modelview.changed_values', 'Parent')
Esempio n. 23
0
class OtEmployee(metaclass=PoolMeta):

    __name__ = 'company.employee'

    employee_list = fields.Many2One('ota.list', 'Employee List')
Esempio n. 24
0
class Line(ModelSQL, ModelView):
    'Analytic Line'
    __name__ = 'analytic_account.line'
    debit = fields.Numeric('Debit',
                           digits=(16, Eval('currency_digits', 2)),
                           required=True,
                           depends=['currency_digits'])
    credit = fields.Numeric('Credit',
                            digits=(16, Eval('currency_digits', 2)),
                            required=True,
                            depends=['currency_digits'])
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
                                      'on_change_with_currency_digits')
    company = fields.Function(fields.Many2One('company.company', 'Company'),
                              'on_change_with_company',
                              searcher='search_company')
    account = fields.Many2One('analytic_account.account',
                              'Account',
                              required=True,
                              select=True,
                              domain=[
                                  ('type', 'not in', ['view', 'distribution']),
                                  [
                                      'OR',
                                      ('company', '=', None),
                                      ('company', '=', Eval('company', -1)),
                                  ],
                              ],
                              depends=['company'])
    move_line = fields.Many2One('account.move.line',
                                'Account Move Line',
                                ondelete='CASCADE',
                                required=True)
    date = fields.Date('Date', required=True)

    @classmethod
    def __setup__(cls):
        super(Line, cls).__setup__()
        t = cls.__table__()
        cls._sql_constraints += [
            ('credit_debit_', Check(t, t.credit * t.debit == 0),
             'account.msg_line_debit_credit'),
        ]
        cls._order.insert(0, ('date', 'ASC'))

    @classmethod
    def __register__(cls, module_name):
        super(Line, cls).__register__(module_name)
        table = cls.__table_handler__(module_name)

        # Migration from 4.0: remove name and journal
        for field_name in ['name', 'journal']:
            table.not_null_action(field_name, action='remove')

        # Migration from 5.0: replace credit_debit constraint by credit_debit_
        table.drop_constraint('credit_debit')

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

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

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

    @fields.depends('move_line', '_parent_move_line.account')
    def on_change_with_currency_digits(self, name=None):
        if self.move_line and self.move_line.account:
            return self.move_line.account.company.currency.digits
        return 2

    @fields.depends('move_line', '_parent_move_line.account')
    def on_change_with_company(self, name=None):
        if self.move_line and self.move_line.account:
            return self.move_line.account.company.id

    @classmethod
    def search_company(cls, name, clause):
        return [('move_line.account.' + clause[0], ) + tuple(clause[1:])]

    @fields.depends('move_line', '_parent_move_line.date',
                    '_parent_move_line.debit', '_parent_move_line.credit')
    def on_change_move_line(self):
        if self.move_line:
            self.date = self.move_line.date
            self.debit = self.move_line.debit
            self.credit = self.move_line.credit

    @staticmethod
    def query_get(table):
        '''
        Return SQL clause for analytic line depending of the context.
        table is the SQL instance of the analytic_account_line table.
        '''
        clause = Literal(True)
        if Transaction().context.get('start_date'):
            clause &= table.date >= Transaction().context['start_date']
        if Transaction().context.get('end_date'):
            clause &= table.date <= Transaction().context['end_date']
        return clause

    @classmethod
    def create(cls, vlist):
        pool = Pool()
        MoveLine = pool.get('account.move.line')
        lines = super(Line, cls).create(vlist)
        move_lines = [l.move_line for l in lines]
        MoveLine.set_analytic_state(move_lines)
        MoveLine.save(move_lines)
        return lines

    @classmethod
    def write(cls, *args):
        pool = Pool()
        MoveLine = pool.get('account.move.line')
        super(Line, cls).write(*args)
        lines = sum(args[0:None:2], [])
        move_lines = [l.move_line for l in lines]
        MoveLine.set_analytic_state(move_lines)
        MoveLine.save(move_lines)

    @classmethod
    def delete(cls, lines):
        pool = Pool()
        MoveLine = pool.get('account.move.line')
        move_lines = [l.move_line for l in lines]
        super(Line, cls).delete(lines)
        MoveLine.set_analytic_state(move_lines)
        MoveLine.save(move_lines)
Esempio n. 25
0
    @classmethod
    def view_attributes(cls):
        storage_types = Eval('type').in_(['storage', 'warehouse', 'view'])
        return super().view_attributes() + [
            ('/tree/field[@name="quantity"]', 'visual',
             If(storage_types &
                (Eval('quantity', 0) < 0), 'danger', ''), ['type']),
            ('/tree/field[@name="forecast_quantity"]', 'visual',
             If(storage_types &
                (Eval('forecast_quantity', 0) < 0), 'warning', ''), ['type']),
        ]


supplier_location = fields.Many2One(
    'stock.location',
    "Supplier Location",
    domain=[('type', '=', 'supplier')],
    help="The default source location for stock received from the party.")
customer_location = fields.Many2One(
    'stock.location',
    "Customer Location",
    domain=[('type', '=', 'customer')],
    help="The default destination location for stock sent to the party.")


class Party(metaclass=PoolMeta):
    __name__ = 'party.party'
    supplier_location = fields.MultiValue(supplier_location)
    customer_location = fields.MultiValue(customer_location)
    locations = fields.One2Many('party.party.location', 'party', "Locations")
Esempio n. 26
0
class HouseRentAllowance(Workflow, ModelSQL, ModelView):
    """House Rent Allowance for an Employee"""

    __name__ = 'hr.allowance.hra'

    salary_code = fields.Char(
        'Salary Code',
        required=True,
        states={'readonly': ~Eval('state').in_(['draft'])},
        depends=['state'])
    employee = fields.Many2One(
        'company.employee',
        'Employee',
        required=True,
        states={'readonly': ~Eval('state').in_(['draft'])},
        depends=['state'])
    designation = fields.Many2One('employee.designation',
                                  'Designation',
                                  required=True)
    department = fields.Many2One(
        'company.department',
        'Department',
        required=True,
    )
    from_date = fields.Date('From Date',
                            states={'readonly': ~Eval('state').in_(['draft'])},
                            depends=['state'])
    to_date = fields.Date('To Date',
                          states={'readonly': ~Eval('state').in_(['draft'])},
                          depends=['state'])
    address = fields.Char('House Address',
                          states={'readonly': ~Eval('state').in_(['draft'])},
                          depends=['state'])
    amount = fields.Float('Expenditure On Rent',
                          required=True,
                          states={'readonly': ~Eval('state').in_(['draft'])},
                          depends=['state'])
    state = fields.Selection([
        ('draft', 'Draft'),
        ('confirm', 'Confirm'),
        ('submit', 'Submit'),
        ('cash_section_officer', 'Cash Section_officer'),
        ('cancel', 'Cancel'),
        ('approve', 'Approve'),
    ],
                             'Status',
                             readonly=True)

    @staticmethod
    def default_from_date():
        return datetime.date.today()

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._buttons.update({
            "submit": {
                'invisible': ~Eval('state').in_(['draft', 'confirm']),
            },
            "cancel": {
                'invisible': ~Eval('state').in_(['cash_section_officer']),
            },
            "approve": {
                'invisible': ~Eval('state').in_(['cash_section_officer']),
            },
        })
        cls._transitions |= set((
            ('draft', 'confirm'),
            ('confirm', 'submit'),
            ('submit', 'cash_section_officer'),
            ('cash_section_officer', 'approve'),
            ('cash_section_officer', 'cancel'),
        ))

    @staticmethod
    def default_state():
        return 'draft'

    @classmethod
    @ModelView.button
    @ModelView.button_action('submit')
    def submit(cls, records):
        pass

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

    @classmethod
    @ModelView.button
    @Workflow.transition('cash_section_officer')
    def submit(cls, records):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('approve')
    def approve(cls, records):
        pass

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

    @fields.depends('employee')
    def on_change_employee(self):
        if self.employee:
            self.salary_code = self.employee.salary_code if self.employee.salary_code else None
            self.designation = self.employee.designation if self.employee.designation else None
            self.department = self.employee.department if self.employee.department else None

    @staticmethod
    def default_employee():
        global current_employee
        current_employee = None
        pool = Pool()
        Employee = pool.get('company.employee')
        employee_id = Transaction().context.get('employee')
        employee = Employee.search([('id', '=', employee_id)])
        if employee != []:
            current_employee = employee[0]
        return current_employee.id if current_employee else None
Esempio n. 27
0
class ProductsByLocations(DeactivableMixin, ModelSQL, ModelView):
    "Products by Locations"
    __name__ = 'stock.products_by_locations'

    product = fields.Many2One('product.product', "Product")
    quantity = fields.Function(fields.Float(
        "Quantity", digits=(16, Eval('default_uom_digits', 2))),
                               'get_product',
                               searcher='search_product')
    forecast_quantity = fields.Function(fields.Float(
        "Forecast Quantity", digits=(16, Eval('default_uom_digits', 2))),
                                        'get_product',
                                        searcher='search_product')
    default_uom = fields.Function(fields.Many2One('product.uom',
                                                  "Default UOM"),
                                  'get_product',
                                  searcher='search_product')
    default_uom_digits = fields.Function(fields.Integer("Default UOM Digits"),
                                         'get_product')
    cost_value = fields.Function(fields.Numeric("Cost Value"), 'get_product')
    consumable = fields.Function(fields.Boolean("Consumable"),
                                 'get_product',
                                 searcher='search_product')

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._order.insert(0, ('product', 'ASC'))

    @classmethod
    def table_query(cls):
        pool = Pool()
        Product = pool.get('product.product')
        product = Product.__table__()
        columns = []
        for fname, field in cls._fields.items():
            if not hasattr(field, 'set'):
                if (isinstance(field, fields.Many2One)
                        and field.get_target() == Product):
                    column = Column(product, 'id')
                else:
                    column = Column(product, fname)
                columns.append(column.as_(fname))
        return product.select(*columns)

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

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

    def get_product(self, name):
        value = getattr(self.product, name)
        if isinstance(value, Model):
            value = value.id
        return value

    @classmethod
    def search_product(cls, name, clause):
        nested = clause[0].lstrip(name)
        return [('product.' + name + nested, ) + tuple(clause[1:])]
Esempio n. 28
0
class Sale:
    "Sale"
    __name__ = 'sale.sale'

    is_international_shipping = fields.Function(
        fields.Boolean("Is International Shipping"),
        'on_change_with_is_international_shipping'
    )
    package_weight = fields.Function(
        fields.Float(
            "Package weight", digits=(16,  Eval('weight_digits', 2)),
            depends=['weight_digits'],
        ),
        'get_package_weight'
    )

    total_weight = fields.Function(
        fields.Float(
            "Total weight", digits=(16,  Eval('weight_digits', 2)),
            depends=['weight_digits'],
        ),
        'get_total_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'
    )

    @classmethod
    def __setup__(cls):
        super(Sale, cls).__setup__()
        cls._error_messages.update({
            'warehouse_address_missing': 'Warehouse address is missing',
        })

    @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 package
        """
        return self._get_weight_uom().id

    def _get_weight_uom(self):
        """
        Returns Pound as default value for uom

        Downstream module can override this method to change weight uom as per
        carrier
        """
        UOM = Pool().get('product.uom')

        return UOM.search([('symbol', '=', 'lb')])[0]

    def get_package_weight(self, name):
        """
        Returns sum of weight associated with each line
        """
        warnings.warn(
            'Field package_weight is depricated, use total_weight instead',
            DeprecationWarning, stacklevel=2
        )
        weight_uom = self._get_weight_uom()
        return self._get_package_weight(weight_uom)

    def get_total_weight(self, name):
        """
        Returns sum of weight associated with each line
        """
        weight_uom = self._get_weight_uom()
        return self._get_total_weight(weight_uom)

    @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()

        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_package_weight(self, uom):
        """
        Returns sum of weight associated with package
        """
        warnings.warn(
            '_get_package_weight is depricated, use _get_total_weight instead',
            DeprecationWarning, stacklevel=2
        )
        return sum(
            map(
                lambda line: line.get_weight(uom, silent=True),
                self.lines
            )
        )

    def _get_total_weight(self, uom):
        """
        Returns sum of weight for given uom
        """
        return sum(
            map(
                lambda line: line.get_weight(uom, silent=True),
                self.lines
            )
        )

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

    def add_shipping_line(self, shipment_cost, description):
        """
        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
        """
        self.__class__.write([self], {
            'lines': [
                ('create', [{
                    'type': 'line',
                    'product': self.carrier.carrier_product.id,
                    'description': description,
                    'quantity': 1,  # XXX
                    'unit': self.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
                ]),
            ]
        })

    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_product_shipping(self):
        """
        This method apply product(carrier) shipping.
        """
        Currency = Pool().get('currency.currency')

        with Transaction().set_context(self._get_carrier_context()):
            shipment_cost, currency_id = self.carrier.get_sale_price()

        shipment_cost = Currency.compute(
            Currency(currency_id), shipment_cost, self.currency
        )
        self.add_shipping_line(shipment_cost, self.carrier.rec_name)

    def get_shipping_rates(self, carrier):
        """
        Return list of tuples as:
            [
                (
                    <display method name>, <cost>, <currency>, <metadata>,
                    <write_vals>
                )
                ...
            ]
        """
        Currency = Pool().get('currency.currency')

        if carrier.carrier_cost_method == 'product':
            with Transaction().set_context(self._get_carrier_context()):
                cost, currency_id = carrier.get_sale_price()
            return [(
                carrier.rec_name,
                cost,
                Currency(currency_id),
                {},
                {'carrier': carrier.id},
            )]
        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 get_all_shipping_rates(self):
        """
        Return shipping rates for all allowed carriers

        Return list of tuples as:
            [
                (
                    <display method name>, <rate>, <currency>, <metadata>,
                    <write_vals>
                )
                ...
            ]
        """
        Carrier = Pool().get('carrier')

        carriers = Carrier.search(self.get_allowed_carriers_domain())
        errors = []
        rate_list = []
        for carrier in carriers:
            try:
                rate_list += self.get_shipping_rates(carrier=carrier)
            except UserError, e:
                # XXX: Collect all errors from shipping carriers
                errors.append(e.message)
        if not rate_list and errors:
            raise self.raise_user_error('\n'.join(errors))
        return rate_list
Esempio n. 29
0
class Identifier(sequence_ordered(), ModelSQL, ModelView):
    'Party Identifier'
    __name__ = 'party.identifier'
    _rec_name = 'code'
    party = fields.Many2One('party.party', 'Party', ondelete='CASCADE',
        required=True, select=True,
        help="The party identified by this record.")
    type = fields.Selection('get_types', 'Type')
    type_string = type.translated('type')
    code = fields.Char('Code', required=True)

    @classmethod
    def __register__(cls, module_name):
        pool = Pool()
        Party = pool.get('party.party')
        cursor = Transaction().connection.cursor()
        party = Party.__table__()
        table = cls.__table__()

        super().__register__(module_name)

        party_h = Party.__table_handler__(module_name)
        if (party_h.column_exist('vat_number')
                and party_h.column_exist('vat_country')):
            identifiers = []
            cursor.execute(*party.select(
                    party.id, party.vat_number, party.vat_country,
                    where=(party.vat_number != Null)
                    | (party.vat_country != Null)))
            for party_id, number, country in cursor:
                code = (country or '') + (number or '')
                if not code:
                    continue
                for type in Party.tax_identifier_types():
                    module = get_cc_module(*type.split('_', 1))
                    if module.is_valid(code):
                        break
                else:
                    type = None
                identifiers.append(
                    cls(party=party_id, code=code, type=type))
            cls.save(identifiers)
            party_h.drop_column('vat_number')
            party_h.drop_column('vat_country')

        # Migration from 5.8: Rename cn_rit into cn_ric
        cursor.execute(*table.update([table.type], ['cn_ric'],
                where=(table.type == 'cn_rit')))

    @classmethod
    def get_types(cls):
        pool = Pool()
        Configuration = pool.get('party.configuration')
        configuration = Configuration(1)
        return [(None, '')] + configuration.get_identifier_types()

    @fields.depends('type', 'code')
    def on_change_with_code(self):
        if self.type and '_' in self.type:
            module = get_cc_module(*self.type.split('_', 1))
            if module:
                try:
                    return module.compact(self.code)
                except stdnum.exceptions.ValidationError:
                    pass
        return self.code

    def pre_validate(self):
        super().pre_validate()
        self.check_code()

    @fields.depends('type', 'party', 'code')
    def check_code(self):
        if self.type and '_' in self.type:
            module = get_cc_module(*self.type.split('_', 1))
            if module:
                if not module.is_valid(self.code):
                    if self.party and self.party.id > 0:
                        party = self.party.rec_name
                    else:
                        party = ''
                    raise InvalidIdentifierCode(
                        gettext('party.msg_invalid_code',
                            type=self.type_string,
                            code=self.code,
                            party=party))
Esempio n. 30
0
class Line(sequence_ordered(), ModelSQL, ModelView):
    "Subscription Line"
    __name__ = 'sale.subscription.line'
    _rec_name = 'description'

    subscription = fields.Many2One(
        'sale.subscription', "Subscription", required=True, select=True,
        ondelete='CASCADE',
        states={
            'readonly': ((Eval('subscription_state') != 'draft')
                & Bool(Eval('subscription'))),
            },
        depends=['subscription_state'],
        help="Add the line below the subscription.")
    subscription_state = fields.Function(
        fields.Selection(STATES, "Subscription State"),
        'on_change_with_subscription_state')
    subscription_start_date = fields.Function(
        fields.Date("Subscription Start Date"),
        'on_change_with_subscription_start_date')
    subscription_end_date = fields.Function(
        fields.Date("Subscription End Date"),
        'on_change_with_subscription_end_date')

    service = fields.Many2One(
        'sale.subscription.service', "Service", required=True,
        states={
            'readonly': Eval('subscription_state') != 'draft',
            },
        depends=['subscription_state'])
    description = fields.Text("Description", required=True,
        states={
            'readonly': Eval('subscription_state') != 'draft',
            },
        depends=['subscription_state'])

    quantity = fields.Float(
        "Quantity", digits=(16, Eval('unit_digits', 2)),
        states={
            'readonly': Eval('subscription_state') != 'draft',
            'required': Bool(Eval('consumption_recurrence')),
            },
        depends=[
            'unit_digits', 'subscription_state', 'consumption_recurrence'])
    unit = fields.Many2One(
        'product.uom', "Unit", required=True,
        states={
            'readonly': Eval('subscription_state') != 'draft',
            },
        domain=[
            If(Bool(Eval('service_unit_category')),
                ('category', '=', Eval('service_unit_category')),
                ('category', '!=', -1)),
            ],
        depends=['subscription_state', 'service_unit_category'])
    unit_digits = fields.Function(
        fields.Integer("Unit Digits"), 'on_change_with_unit_digits')
    service_unit_category = fields.Function(
        fields.Many2One('product.uom.category', "Service Unit Category"),
        'on_change_with_service_unit_category')

    unit_price = fields.Numeric(
        "Unit Price", digits=price_digits,
        states={
            'readonly': Eval('subscription_state') != 'draft',
            },
        depends=['subscription_state'])

    consumption_recurrence = fields.Many2One(
        'sale.subscription.recurrence.rule.set', "Consumption Recurrence",
        states={
            'readonly': Eval('subscription_state') != 'draft',
            },
        depends=['subscription_state'])
    consumption_delay = fields.TimeDelta(
        "Consumption Delay",
        states={
            'readonly': Eval('subscription_state') != 'draft',
            'invisible': ~Eval('consumption_recurrence'),
            },
        depends=['subscription_state', 'consumption_recurrence'])
    next_consumption_date = fields.Date("Next Consumption Date", readonly=True)
    next_consumption_date_delayed = fields.Function(
        fields.Date("Next Consumption Delayed"),
        'get_next_consumption_date_delayed')
    consumed = fields.Boolean("Consumed")
    start_date = fields.Date(
        "Start Date",
        domain=['OR',
            ('start_date', '>=', Eval('subscription_start_date')),
            ('start_date', '=', None),
            ],
        states={
            'readonly': ((Eval('subscription_state') != 'draft')
                | Eval('consumed')),
            },
        depends=['subscription_start_date', 'subscription_state', 'consumed'])
    end_date = fields.Date(
        "End Date",
        domain=['OR', [
                If(Bool(Eval('subscription_end_date')),
                    ('end_date', '<=', Eval('subscription_end_date')),
                    ('end_date', '>=', Eval('start_date'))),
                If(Bool(Eval('next_consumption_date')),
                    ('end_date', '>=', Eval('next_consumption_date')),
                    ('end_date', '>=', Eval('start_date'))),
                ],
            ('end_date', '=', None),
            ],
        states={
            'readonly': ((Eval('subscription_state') != 'draft')
                | (~Eval('next_consumption_date') & Eval('consumed'))),
            },
        depends=['subscription_end_date', 'start_date',
            'next_consumption_date', 'subscription_state', 'consumed'])

    @fields.depends('subscription', '_parent_subscription.state')
    def on_change_with_subscription_state(self, name=None):
        if self.subscription:
            return self.subscription.state

    @fields.depends('subscription', '_parent_subscription.start_date')
    def on_change_with_subscription_start_date(self, name=None):
        if self.subscription:
            return self.subscription.start_date

    @fields.depends('subscription', '_parent_subscription.end_date')
    def on_change_with_subscription_end_date(self, name=None):
        if self.subscription:
            return self.subscription.end_date

    @classmethod
    def default_quantity(cls):
        return 1

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

    @fields.depends('service')
    def on_change_with_service_unit_category(self, name=None):
        if self.service:
            return self.service.product.default_uom_category.id

    @fields.depends('service', 'quantity', 'unit', 'description',
        'subscription', '_parent_subscription.currency',
        '_parent_subscription.party', '_parent_subscription.start_date')
    def on_change_service(self):
        pool = Pool()
        Product = pool.get('product.product')

        if not self.service:
            self.consumption_recurrence = None
            self.consumption_delay = None
            return

        party = None
        party_context = {}
        if self.subscription and self.subscription.party:
            party = self.subscription.party
            if party.lang:
                party_context['language'] = party.lang.code

        product = self.service.product
        category = product.sale_uom.category
        if not self.unit or self.unit.category != category:
            self.unit = product.sale_uom
            self.unit_digits = product.sale_uom.digits

        with Transaction().set_context(self._get_context_sale_price()):
            self.unit_price = Product.get_sale_price(
                [product], self.quantity or 0)[product.id]
            if self.unit_price:
                self.unit_price = self.unit_price.quantize(
                    Decimal(1) / 10 ** self.__class__.unit_price.digits[1])

        if not self.description:
            with Transaction().set_context(party_context):
                self.description = Product(product.id).rec_name

        self.consumption_recurrence = self.service.consumption_recurrence
        self.consumption_delay = self.service.consumption_delay

    def _get_context_sale_price(self):
        context = {}
        if getattr(self, 'subscription', None):
            if getattr(self.subscription, 'currency', None):
                context['currency'] = self.subscription.currency.id
            if getattr(self.subscription, 'party', None):
                context['customer'] = self.subscription.party.id
            if getattr(self.subscription, 'start_date', None):
                context['sale_date'] = self.subscription.start_date
        if self.unit:
            context['uom'] = self.unit.id
        elif self.service:
            context['uom'] = self.service.sale_uom.id
        # TODO tax
        return context

    def get_next_consumption_date_delayed(self, name=None):
        if self.next_consumption_date and self.consumption_delay:
            return self.next_consumption_date + self.consumption_delay
        return self.next_consumption_date

    @classmethod
    def default_consumed(cls):
        return False

    @classmethod
    def domain_next_consumption_date_delayed(cls, domain, tables):
        field = cls.next_consumption_date_delayed._field
        table, _ = tables[None]
        name, operator, value = domain
        Operator = fields.SQL_OPERATORS[operator]
        column = (
            table.next_consumption_date + Coalesce(
                table.consumption_delay, datetime.timedelta()))
        expression = Operator(column, field._domain_value(operator, value))
        if isinstance(expression, operators.In) and not expression.right:
            expression = Literal(False)
        elif isinstance(expression, operators.NotIn) and not expression.right:
            expression = Literal(True)
        expression = field._domain_add_null(
            column, operator, value, expression)
        return expression

    @classmethod
    def generate_consumption(cls, date=None):
        pool = Pool()
        Date = pool.get('ir.date')
        Consumption = pool.get('sale.subscription.line.consumption')
        Subscription = pool.get('sale.subscription')

        if date is None:
            date = Date.today()

        remainings = all_lines = cls.search([
                ('consumption_recurrence', '!=', None),
                ('next_consumption_date_delayed', '<=', date),
                ('subscription.state', '=', 'running'),
                ])

        consumptions = []
        subscription_ids = set()
        while remainings:
            lines, remainings = remainings, []
            for line in lines:
                consumptions.append(
                    line.get_consumption(line.next_consumption_date))
                line.next_consumption_date = (
                    line.compute_next_consumption_date())
                line.consumed = True
                if line.next_consumption_date is None:
                    subscription_ids.add(line.subscription.id)
                elif line.get_next_consumption_date_delayed() <= date:
                    remainings.append(line)

        Consumption.save(consumptions)
        cls.save(all_lines)
        Subscription.process(Subscription.browse(list(subscription_ids)))

    def get_consumption(self, date):
        pool = Pool()
        Consumption = pool.get('sale.subscription.line.consumption')
        return Consumption(line=self, quantity=self.quantity, date=date)

    def compute_next_consumption_date(self):
        if not self.consumption_recurrence:
            return None
        start_date = self.start_date or self.subscription.start_date
        date = self.next_consumption_date or start_date
        rruleset = self.consumption_recurrence.rruleset(start_date)
        dt = datetime.datetime.combine(date, datetime.time())
        inc = (start_date == date) and not self.next_consumption_date
        next_date = rruleset.after(dt, inc=inc).date()
        for end_date in [self.end_date, self.subscription.end_date]:
            if end_date:
                if next_date > end_date:
                    return None
        return next_date

    @classmethod
    def copy(cls, lines, default=None):
        if default is None:
            default = {}
        else:
            default = default.copy()
        default.setdefault('consumed')
        return super(Line, cls).copy(lines, default=default)