Beispiel #1
0
class PartySOM(ModelSQL, ModelView):
    _name = "ekd.party.som"

    name = fields.Char('State of Mind', size=64, required=True)
    factor = fields.Float('Factor', required=True)
Beispiel #2
0
class Work(sequence_ordered(), tree(separator='\\'), ModelSQL, ModelView):
    'Work Effort'
    __name__ = 'project.work'
    name = fields.Char('Name', required=True, select=True)
    type = fields.Selection([
            ('project', 'Project'),
            ('task', 'Task')
            ],
        'Type', required=True, select=True)
    company = fields.Many2One('company.company', 'Company', required=True,
        select=True)
    party = fields.Many2One('party.party', 'Party',
        states={
            'invisible': Eval('type') != 'project',
            },
        context={
            'company': Eval('company', -1),
            },
        depends={'company'})
    party_address = fields.Many2One('party.address', 'Contact Address',
        domain=[('party', '=', Eval('party'))],
        states={
            'invisible': Eval('type') != 'project',
            })
    timesheet_works = fields.One2Many(
        'timesheet.work', 'origin', 'Timesheet Works', readonly=True, size=1)
    timesheet_available = fields.Function(
        fields.Boolean('Available on timesheets'),
        'get_timesheet_available', setter='set_timesheet_available')
    timesheet_start_date = fields.Function(fields.Date('Timesheet Start',
            states={
                'invisible': ~Eval('timesheet_available'),
                }),
        'get_timesheet_date', setter='set_timesheet_date')
    timesheet_end_date = fields.Function(fields.Date('Timesheet End',
            states={
                'invisible': ~Eval('timesheet_available'),
                }),
        'get_timesheet_date', setter='set_timesheet_date')
    timesheet_duration = fields.Function(fields.TimeDelta('Duration',
            'company_work_time',
            help="Total time spent on this work and the sub-works."),
        'get_total')
    effort_duration = fields.TimeDelta('Effort', 'company_work_time',
        help="Estimated Effort for this work.")
    total_effort = fields.Function(fields.TimeDelta('Total Effort',
            'company_work_time',
            help="Estimated total effort for this work and the sub-works."),
        'get_total')
    progress = fields.Float('Progress',
        domain=['OR',
            ('progress', '=', None),
            [
                ('progress', '>=', 0),
                ('progress', '<=', 1),
                ],
            ],
        help='Estimated progress for this work.')
    total_progress = fields.Function(fields.Float('Total Progress',
            digits=(16, 4),
            help='Estimated total progress for this work and the sub-works.',
            states={
                'invisible': (
                    Eval('total_progress', None) == None),  # noqa: E711
                }),
        'get_total')
    comment = fields.Text('Comment')
    parent = fields.Many2One(
        'project.work', 'Parent', path='path', ondelete='RESTRICT',
        domain=[
            ('company', '=', Eval('company', -1)),
            ])
    path = fields.Char("Path", select=True)
    children = fields.One2Many('project.work', 'parent', 'Children',
        domain=[
            ('company', '=', Eval('company', -1)),
            ])
    status = fields.Many2One(
        'project.work.status', "Status", required=True, select=True,
        domain=[If(Bool(Eval('type')), ('types', 'in', Eval('type')), ())])

    @staticmethod
    def default_type():
        return 'task'

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

    @classmethod
    def default_status(cls):
        pool = Pool()
        WorkStatus = pool.get('project.work.status')
        return WorkStatus.get_default_status(cls.default_type())

    @classmethod
    def __register__(cls, module_name):
        TimesheetWork = Pool().get('timesheet.work')
        transaction = Transaction()
        cursor = transaction.connection.cursor()
        update = transaction.connection.cursor()
        table_project_work = cls.__table_handler__(module_name)
        project = cls.__table__()
        timesheet = TimesheetWork.__table__()

        work_exist = table_project_work.column_exist('work')
        add_parent = (not table_project_work.column_exist('parent')
            and work_exist)
        add_company = (not table_project_work.column_exist('company')
            and work_exist)
        add_name = (not table_project_work.column_exist('name')
            and work_exist)

        super(Work, cls).__register__(module_name)

        # Migration from 3.4: change effort into timedelta effort_duration
        if table_project_work.column_exist('effort'):
            cursor.execute(*project.select(project.id, project.effort,
                    where=project.effort != Null))
            for id_, effort in cursor:
                duration = datetime.timedelta(hours=effort)
                update.execute(*project.update(
                        [project.effort_duration],
                        [duration],
                        where=project.id == id_))
            table_project_work.drop_column('effort')

        # Migration from 3.6: add parent, company, drop required on work,
        # fill name
        if add_parent:
            second_project = cls.__table__()
            query = project.join(timesheet,
                condition=project.work == timesheet.id
                ).join(second_project,
                    condition=timesheet.parent == second_project.work
                    ).select(project.id, second_project.id)
            cursor.execute(*query)
            for id_, parent in cursor:
                update.execute(*project.update(
                        [project.parent],
                        [parent],
                        where=project.id == id_))
            cls._rebuild_tree('parent', None, 0)
        if add_company:
            cursor.execute(*project.join(timesheet,
                    condition=project.work == timesheet.id
                    ).select(project.id, timesheet.company))
            for id_, company in cursor:
                update.execute(*project.update(
                        [project.company],
                        [company],
                        where=project.id == id_))
        table_project_work.not_null_action('work', action='remove')
        if add_name:
            cursor.execute(*project.join(timesheet,
                    condition=project.work == timesheet.id
                    ).select(project.id, timesheet.name))
            for id_, name in cursor:
                update.execute(*project.update(
                        [project.name],
                        [name],
                        where=project.id == id_))

        # Migration from 4.0: remove work
        if work_exist:
            table_project_work.drop_constraint('work_uniq')
            cursor.execute(*project.select(project.id, project.work,
                    where=project.work != Null))
            for project_id, work_id in cursor:
                update.execute(*timesheet.update(
                        [timesheet.origin, timesheet.name],
                        ['%s,%s' % (cls.__name__, project_id), Null],
                        where=timesheet.id == work_id))
            table_project_work.drop_column('work')

        # Migration from 5.4: replace state by status
        table_project_work.not_null_action('state', action='remove')

        # Migration from 6.0: remove left and right
        table_project_work.drop_column('left')
        table_project_work.drop_column('right')

    @fields.depends('type', 'status')
    def on_change_type(self):
        pool = Pool()
        WorkState = pool.get('project.work.status')
        if (self.type
                and (not self.status
                    or self.type not in self.status.types)):
            self.status = WorkState.get_default_status(self.type)

    @fields.depends('status', 'progress')
    def on_change_status(self):
        if (self.status
                and self.status.progress is not None
                and self.status.progress > (self.progress or -1.0)):
            self.progress = self.status.progress

    @classmethod
    def index_set_field(cls, name):
        index = super(Work, cls).index_set_field(name)
        if name in {'timesheet_start_date', 'timesheet_end_date'}:
            index = cls.index_set_field('timesheet_available') + 1
        return index

    @classmethod
    def validate(cls, works):
        super(Work, cls).validate(works)
        for work in works:
            work.check_work_progress()

    def check_work_progress(self):
        pool = Pool()
        progress = -1 if self.progress is None else self.progress
        if (self.status.progress is not None
                and progress < self.status.progress):
            Lang = pool.get('ir.lang')
            lang = Lang.get()
            raise WorkProgressValidationError(
                gettext('project.msg_work_invalid_progress_status',
                    work=self.rec_name,
                    progress=lang.format('%.2f%%', self.status.progress * 100),
                    status=self.status.rec_name))
        if (self.status.progress == 1.0
                and not all(c.progress == 1.0 for c in self.children)):
            raise WorkProgressValidationError(
                gettext('project.msg_work_children_progress',
                    work=self.rec_name,
                    status=self.status.rec_name))
        if (self.parent
                and self.parent.progress == 1.0
                and not self.progress == 1.0):
            raise WorkProgressValidationError(
                gettext('project.msg_work_parent_progress',
                    work=self.rec_name,
                    parent=self.parent.rec_name))

    @property
    def effort_hours(self):
        if not self.effort_duration:
            return 0
        return self.effort_duration.total_seconds() / 60 / 60

    @property
    def total_effort_hours(self):
        if not self.total_effort:
            return 0
        return self.total_effort.total_seconds() / 60 / 60

    @property
    def timesheet_duration_hours(self):
        if not self.timesheet_duration:
            return 0
        return self.timesheet_duration.total_seconds() / 60 / 60

    @classmethod
    def default_timesheet_available(cls):
        return False

    def get_timesheet_available(self, name):
        return bool(self.timesheet_works)

    @classmethod
    def set_timesheet_available(cls, projects, name, value):
        pool = Pool()
        Timesheet = pool.get('timesheet.work')

        to_create = []
        to_delete = []
        for project in projects:
            if not project.timesheet_works and value:
                to_create.append({
                        'origin': str(project),
                        'company': project.company.id,
                        })
            elif project.timesheet_works and not value:
                to_delete.extend(project.timesheet_works)

        if to_create:
            Timesheet.create(to_create)
        if to_delete:
            Timesheet.delete(to_delete)

    def get_timesheet_date(self, name):
        if self.timesheet_works:
            func = {
                'timesheet_start_date': min,
                'timesheet_end_date': max,
                }[name]
            return func(getattr(w, name) for w in self.timesheet_works)

    @classmethod
    def set_timesheet_date(cls, projects, name, value):
        pool = Pool()
        Timesheet = pool.get('timesheet.work')
        timesheets = [w for p in projects for w in p.timesheet_works]
        if timesheets:
            Timesheet.write(timesheets, {
                    name: value,
                    })

    @classmethod
    def sum_tree(cls, works, values, parents):
        result = values.copy()
        works = set((w.id for w in works))
        leafs = works - set(parents.values())
        while leafs:
            for work in leafs:
                works.remove(work)
                parent = parents.get(work)
                if parent in result:
                    result[parent] += result[work]
            next_leafs = set(works)
            for work in works:
                parent = parents.get(work)
                if not parent:
                    continue
                if parent in next_leafs and parent in works:
                    next_leafs.remove(parent)
            leafs = next_leafs
        return result

    @classmethod
    def get_total(cls, works, names):
        cursor = Transaction().connection.cursor()
        table = cls.__table__()

        works = cls.search([
                ('parent', 'child_of', [w.id for w in works]),
                ])
        work_ids = [w.id for w in works]
        parents = {}
        for sub_ids in grouped_slice(work_ids):
            where = reduce_ids(table.id, sub_ids)
            cursor.execute(*table.select(table.id, table.parent,
                    where=where))
            parents.update(cursor)

        if 'total_progress' in names and 'total_effort' not in names:
            names = list(names)
            names.append('total_effort')

        result = {}
        for name in names:
            values = getattr(cls, '_get_%s' % name)(works)
            result[name] = cls.sum_tree(works, values, parents)

        if 'total_progress' in names:
            digits = cls.total_progress.digits[1]
            total_progress = result['total_progress']
            total_effort = result['total_effort']
            for work in works:
                if total_effort[work.id]:
                    total_progress[work.id] = round(total_progress[work.id]
                        / (total_effort[work.id].total_seconds() / 60 / 60),
                        digits)
                else:
                    total_effort[work.id] = None
        return result

    @classmethod
    def _get_total_effort(cls, works):
        return {w.id: w.effort_duration or datetime.timedelta() for w in works}

    @classmethod
    def _get_timesheet_duration(cls, works):
        durations = {}
        for work in works:
            value = datetime.timedelta()
            for timesheet_work in work.timesheet_works:
                if timesheet_work.duration:
                    value += timesheet_work.duration
            durations[work.id] = value
        return durations

    @classmethod
    def _get_total_progress(cls, works):
        return {w.id: w.effort_hours * (w.progress or 0) for w in works}

    @classmethod
    def copy(cls, project_works, default=None):
        pool = Pool()
        WorkStatus = pool.get('project.work.status')
        if default is None:
            default = {}
        else:
            default = default.copy()
        default.setdefault('children', None)
        default.setdefault('progress', None)
        default.setdefault(
            'status', lambda data: WorkStatus.get_default_status(data['type']))
        return super().copy(project_works, default=default)

    @classmethod
    def delete(cls, project_works):
        TimesheetWork = Pool().get('timesheet.work')

        # Get the timesheet works linked to the project works
        timesheet_works = [
            w for pw in project_works for w in pw.timesheet_works]

        super(Work, cls).delete(project_works)

        if timesheet_works:
            with Transaction().set_context(_check_access=False):
                TimesheetWork.delete(timesheet_works)

    @classmethod
    def search_global(cls, text):
        for record, rec_name, icon in super(Work, cls).search_global(text):
            icon = icon or 'tryton-project'
            yield record, rec_name, icon
Beispiel #3
0
class FloatRequired(ModelSQL):
    'Float Required'
    __name__ = 'test.float_required'
    float = fields.Float(string='Float', help='Test float',
            required=True)
Beispiel #4
0
class PriceListLine(ModelSQL, ModelView, MatchMixin):
    'Price List Line'
    __name__ = 'product.price_list.line'

    price_list = fields.Many2One('product.price_list',
                                 'Price List',
                                 required=True,
                                 ondelete='CASCADE')
    product = fields.Many2One('product.product', 'Product')
    sequence = fields.Integer('Sequence')
    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')
    formula = fields.Char(
        'Formula',
        required=True,
        help=('Python expression that will be evaluated with:\n'
              '- unit_price: the original unit_price'))

    @classmethod
    def __setup__(cls):
        super(PriceListLine, cls).__setup__()
        cls._order.insert(0, ('price_list', 'ASC'))
        cls._order.insert(0, ('sequence', 'ASC'))
        cls._error_messages.update({
            'invalid_formula':
            ('Invalid formula "%(formula)s" in price '
             'list line "%(line)s" with exception "%(exception)s".'),
        })

    @classmethod
    def __register__(cls, module_name):
        TableHandler = backend.get('TableHandler')
        cursor = Transaction().cursor
        table = TableHandler(cursor, cls, module_name)

        super(PriceListLine, cls).__register__(module_name)

        # Migration from 2.4: drop required on sequence
        table.not_null_action('sequence', action='remove')

    @staticmethod
    def order_sequence(tables):
        table, _ = tables[None]
        return [table.sequence == Null, table.sequence]

    @staticmethod
    def default_formula():
        return 'unit_price'

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

    @classmethod
    def validate(cls, lines):
        super(PriceListLine, cls).validate(lines)
        for line in lines:
            line.check_formula()

    def check_formula(self):
        '''
        Check formula
        '''
        context = self.price_list.get_context_formula(None, None, Decimal('0'),
                                                      0, None)

        try:
            if not isinstance(self.get_unit_price(**context), Decimal):
                raise ValueError
        except Exception, exception:
            self.raise_user_error(
                'invalid_formula', {
                    'formula': self.formula,
                    'line': self.rec_name,
                    'exception': exception,
                })
Beispiel #5
0
class Move:
    __name__ = 'stock.move'
    in_anglo_saxon_quantity = fields.Float('Input Anglo-Saxon Quantity',
                                           required=True,
                                           digits=(16, Eval('unit_digits', 2)),
                                           depends=['unit_digits'])
    out_anglo_saxon_quantity = fields.Float('Output Anglo-Saxon Quantity',
                                            required=True,
                                            digits=(16, Eval('unit_digits',
                                                             2)),
                                            depends=['unit_digits'])

    @classmethod
    def __setup__(cls):
        super(Move, cls).__setup__()
        cls._allow_modify_closed_period.update(
            ['in_anglo_saxon_quantity', 'out_anglo_saxon_quantity'])
        cls._sql_constraints += [
            ('check_in_anglo_saxon_quantity',
             'CHECK(quantity >= in_anglo_saxon_quantity)',
             'Anglo-Saxon quantity can not be greater than quantity.'),
            ('check_out_anglo_saxon_quantity',
             'CHECK(quantity >= out_anglo_saxon_quantity)',
             'Anglo-Saxon quantity can not be greater than quantity.'),
        ]

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

        super(Move, cls).__register__(module_name)
        table = TableHandler(cursor, cls, module_name)

        # Migration from 2.8: split anglo_saxon_quantity
        if table.column_exist('anglo_saxon_quantity'):
            cursor.execute(
                'UPDATE "' + cls._table + '" '
                'SET in_anglo_saxon_quantity = anglo_saxon_quantity, '
                'out_anglo_saxon_quantity = anglo_saxon_quantity')
            table.drop_constraint('check_anglo_saxon_quantity')
            table.drop_column('anglo_saxon_quantity')

    @staticmethod
    def default_in_anglo_saxon_quantity():
        return 0.0

    @staticmethod
    def default_out_anglo_saxon_quantity():
        return 0.0

    def _get_account_stock_move_lines(self, type_):
        pool = Pool()
        Uom = pool.get('product.uom')
        AccountMoveLine = pool.get('account.move.line')
        Currency = pool.get('currency.currency')
        lines = super(Move, self)._get_account_stock_move_lines(type_)
        if (type_.endswith('supplier')
                and self.product.cost_price_method == 'fixed'):
            cost_price = Uom.compute_price(self.product.default_uom,
                                           self.cost_price, self.uom)
            with Transaction().set_context(date=self.effective_date):
                unit_price = Currency.compute(self.currency,
                                              self.unit_price,
                                              self.company.currency,
                                              round=False)
            amount = self.company.currency.round(
                Decimal(str(self.quantity)) * (unit_price - cost_price))
            if self.company.currency.is_zero(amount):
                return lines
            account = self.product.account_stock_supplier_used
            for move_line in lines:
                if move_line.account == account:
                    break
            else:
                return lines
            if type_.startswith('in_'):
                move_line.credit += amount
                debit = amount
                credit = Decimal(0)
            else:
                move_line.debit += amount
                debit = Decimal(0)
                credit = amount
            if amount < Decimal(0):
                debit, credit = -credit, -debit
            move_line = AccountMoveLine(
                debit=debit,
                credit=credit,
                account=self.product.account_expense_used,
            )
            lines.append(move_line)
        return lines

    @classmethod
    def _get_anglo_saxon_move(cls, moves, quantity, type_):
        '''
        Generator of (move, qty, cost_price) where move is the move to be
        consumed, qty is the quantity (in the product default uom) to be
        consumed on this move and cost_price is in the company currency.
        '''
        pool = Pool()
        Uom = pool.get('product.uom')
        Currency = pool.get('currency.currency')

        as_qty_field = _get_field(type_)

        consumed_qty = 0.0
        for move in moves:
            qty = Uom.compute_qty(move.uom,
                                  move.quantity - getattr(move, as_qty_field),
                                  move.product.default_uom,
                                  round=False)
            if qty <= 0.0:
                continue
            if qty > quantity - consumed_qty:
                qty = quantity - consumed_qty
            if consumed_qty >= quantity:
                break

            if type_ == 'in_supplier':
                with Transaction().set_context(date=move.effective_date):
                    unit_price = Currency.compute(move.currency,
                                                  move.unit_price,
                                                  move.company.currency,
                                                  round=False)
                cost_price = Uom.compute_price(move.uom, unit_price,
                                               move.product.default_uom)
            else:
                cost_price = move.cost_price

            yield (move, qty, cost_price)
            consumed_qty += qty

    @classmethod
    def update_anglo_saxon_quantity_product_cost(cls, product, moves, quantity,
                                                 uom, type_):
        '''
        Return the cost for quantity based on lines.
        Update anglo_saxon_quantity on the concerned moves.
        '''
        pool = Pool()
        Uom = pool.get('product.uom')

        for move in moves:
            assert move.product == product, 'wrong product'
        assert type_.startswith('in_') or type_.startswith('out_'), \
            'wrong type'

        total_qty = Uom.compute_qty(uom,
                                    quantity,
                                    product.default_uom,
                                    round=False)

        as_qty_field = _get_field(type_)
        cost = Decimal('0.0')
        consumed_qty = 0.0
        for move, move_qty, move_cost_price in cls._get_anglo_saxon_move(
                moves, total_qty, type_):
            consumed_qty += move_qty

            cost += move_cost_price * Decimal(str(move_qty))

            move_qty = Uom.compute_qty(product.default_uom, move_qty, move.uom)
            cls.write([move], {
                as_qty_field:
                ((getattr(move, as_qty_field) or 0.0) + move_qty),
            })

        if consumed_qty < total_qty:
            qty = total_qty - consumed_qty
            consumed_qty += qty
            cost += product.cost_price * Decimal(str(qty))
        return cost

    @classmethod
    def copy(cls, moves, default=None):
        if default is None:
            default = {}
        default = default.copy()
        for prefix in ('in_', 'out_'):
            default.setdefault(
                prefix + 'anglo_saxon_quantity',
                getattr(cls, 'default_%sanglo_saxon_quantity' % prefix)())
        return super(Move, cls).copy(moves, default=default)
Beispiel #6
0
    class OrderLineComponentMixin(ModelStorage):

        line = fields.Many2One(prefix + '.line',
                               "Line",
                               required=True,
                               ondelete='CASCADE',
                               domain=[
                                   ('product.type', '=', 'kit'),
                               ])
        moves = fields.One2Many('stock.move', 'origin', 'Moves', readonly=True)
        moves_ignored = fields.Many2Many(prefix +
                                         '.line.component-ignored-stock.move',
                                         'component',
                                         'move',
                                         "Ignored Moves",
                                         readonly=True)
        moves_recreated = fields.Many2Many(
            prefix + '.line.component-recreated-stock.move',
            'component',
            'move',
            "Recreated Moves",
            readonly=True)
        move_done = fields.Function(fields.Boolean('Moves Done'),
                                    'get_move_done')
        move_exception = fields.Function(fields.Boolean('Moves Exception'),
                                         'get_move_exception')
        quantity_ratio = fields.Float("Quantity Ratio",
                                      readonly=True,
                                      states={
                                          'required': ~Eval('fixed', False),
                                      })
        price_ratio = fields.Numeric("Price Ratio",
                                     readonly=True,
                                     required=True)

        @classmethod
        def __setup__(cls):
            super().__setup__()
            cls.__access__.add('line')

        @fields.depends('line', '_parent_line.product')
        def on_change_with_parent_type(self, name=None):
            if self.line and self.line.product:
                return self.line.product.type

        @classmethod
        def set_price_ratio(cls, components):
            "Set price ratio between components"
            pool = Pool()
            Uom = pool.get('product.uom')
            list_prices = defaultdict(Decimal)
            sum_ = 0
            for component in components:
                product = component.product
                list_price = Uom.compute_price(
                    product.default_uom, product.list_price,
                    component.unit) * Decimal(str(component.quantity))
                list_prices[component] = list_price
                sum_ += list_price

            for component in components:
                if sum_:
                    ratio = list_prices[component] / sum_
                else:
                    ratio = 1 / len(components)
                component.price_ratio = ratio

        def get_move(self, type_):
            raise NotImplementedError

        def _get_shipped_quantity(self, shipment_type):
            'Return the quantity already shipped'
            pool = Pool()
            Uom = pool.get('product.uom')

            quantity = 0
            skips = set(self.moves_recreated)
            for move in self.moves:
                if move not in skips:
                    quantity += Uom.compute_qty(move.uom, move.quantity,
                                                self.unit)
            return quantity

        @property
        def _move_remaining_quantity(self):
            "Compute the remaining quantity to ship"
            pool = Pool()
            Uom = pool.get('product.uom')
            ignored = set(self.moves_ignored)
            quantity = abs(self.quantity)
            for move in self.moves:
                if move.state == 'done' or move in ignored:
                    quantity -= Uom.compute_qty(move.uom, move.quantity,
                                                self.unit)
            return quantity

        def get_move_done(self, name):
            quantity = self._move_remaining_quantity
            if quantity is None:
                return True
            else:
                return self.unit.round(quantity) <= 0

        def get_move_exception(self, name):
            skips = set(self.moves_ignored)
            skips.update(self.moves_recreated)
            for move in self.moves:
                if move.state == 'cancelled' and move not in skips:
                    return True
            return False

        def get_moved_ratio(self):
            pool = Pool()
            Uom = pool.get('product.uom')

            quantity = 0
            for move in self.moves:
                if move.state != 'done':
                    continue
                qty = Uom.compute_qty(move.uom, move.quantity, self.unit)
                dest_type = self.line.to_location.type
                if (move.to_location.type == dest_type
                        and move.from_location.type != dest_type):
                    quantity += qty
                elif (move.from_location.type == dest_type
                      and move.to_location.type != dest_type):
                    quantity -= qty
            if self.quantity < 0:
                quantity *= -1
            return quantity / self.quantity

        def get_rec_name(self, name):
            return super().get_rec_name(name) + (' @ %s' % self.line.rec_name)

        @classmethod
        def search_rec_name(cls, name, clause):
            return super().search_rec_name(name, clause) + [
                ('line.rec_name', ) + tuple(clause[1:]),
            ]
Beispiel #7
0
class InventoryLine(ModelSQL, ModelView):
    'Stock Inventory Line'
    __name__ = 'stock.inventory.line'
    _states = {
        'readonly': Eval('inventory_state') != 'draft',
    }
    _depends = ['inventory_state']

    product = fields.Many2One('product.product',
                              'Product',
                              required=True,
                              domain=[
                                  ('type', '=', 'goods'),
                              ],
                              states=_states,
                              depends=_depends)
    uom = fields.Function(
        fields.Many2One('product.uom',
                        'UOM',
                        help="The unit in which the quantity is specified."),
        'get_uom')
    unit_digits = fields.Function(fields.Integer('Unit Digits'),
                                  'get_unit_digits')
    expected_quantity = fields.Float(
        'Expected Quantity',
        required=True,
        digits=(16, Eval('unit_digits', 2)),
        readonly=True,
        states={
            'invisible': Eval('id', -1) < 0,
        },
        depends=['unit_digits'],
        help="The quantity the system calculated should be in the location.")
    quantity = fields.Float('Quantity',
                            digits=(16, Eval('unit_digits', 2)),
                            states=_states,
                            depends=['unit_digits'] + _depends,
                            help="The actual quantity found in the location.")
    moves = fields.One2Many('stock.move', 'origin', 'Moves', readonly=True)
    inventory = fields.Many2One('stock.inventory',
                                'Inventory',
                                required=True,
                                ondelete='CASCADE',
                                states={
                                    'readonly':
                                    _states['readonly']
                                    & Bool(Eval('inventory')),
                                },
                                depends=_depends,
                                help="The inventory the line belongs to.")
    inventory_location = fields.Function(fields.Many2One(
        'stock.location', "Location"),
                                         'on_change_with_inventory_location',
                                         searcher='search_inventory_location')
    inventory_date = fields.Function(fields.Date("Date"),
                                     'on_change_with_inventory_date',
                                     searcher='search_inventory_date')
    inventory_state = fields.Function(
        fields.Selection('get_inventory_states', "Inventory State"),
        'on_change_with_inventory_state')

    @classmethod
    def __setup__(cls):
        super(InventoryLine, cls).__setup__()
        t = cls.__table__()
        cls._sql_constraints += [
            ('check_line_qty_pos', Check(t, t.quantity >= 0),
             'stock.msg_inventory_line_quantity_positive'),
        ]
        cls._order.insert(0, ('product', 'ASC'))

    @classmethod
    def __register__(cls, module_name):
        cursor = Transaction().connection.cursor()
        pool = Pool()
        Move = pool.get('stock.move')
        sql_table = cls.__table__()
        move_table = Move.__table__()

        super(InventoryLine, cls).__register__(module_name)

        table = cls.__table_handler__(module_name)

        # Migration from 3.0: use Move origin
        if table.column_exist('move'):
            cursor.execute(*sql_table.select(
                sql_table.id, sql_table.move, where=sql_table.move != Null))
            for line_id, move_id in cursor.fetchall():
                cursor.execute(*move_table.update(
                    columns=[move_table.origin],
                    values=['%s,%s' % (cls.__name__, line_id)],
                    where=move_table.id == move_id))
            table.drop_column('move')

        # Migration from 4.6: drop required on quantity
        table.not_null_action('quantity', action='remove')

    @staticmethod
    def default_unit_digits():
        return 2

    @staticmethod
    def default_expected_quantity():
        return 0.

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

    @fields.depends('inventory', '_parent_inventory.location')
    def on_change_with_inventory_location(self, name=None):
        if self.inventory and self.inventory.location:
            return self.inventory.location.id

    @classmethod
    def search_inventory_location(cls, name, clause):
        nested = clause[0].lstrip(name)
        return [('inventory.' + name + nested, ) + tuple(clause[1:])]

    @fields.depends('inventory', '_parent_inventory.date')
    def on_change_with_inventory_date(self, name=None):
        if self.inventory:
            return self.inventory.date

    @classmethod
    def search_inventory_date(cls, name, clause):
        return [('inventory.date', ) + tuple(clause[1:])]

    @classmethod
    def get_inventory_states(cls):
        pool = Pool()
        Inventory = pool.get('stock.inventory')
        return Inventory.fields_get(['state'])['state']['selection']

    @fields.depends('inventory', '_parent_inventory.state')
    def on_change_with_inventory_state(self, name=None):
        if self.inventory:
            return self.inventory.state
        return 'draft'

    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_uom(self, name):
        return self.product.default_uom.id

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

    @property
    def unique_key(self):
        key = []
        for fname in self.inventory.grouping():
            value = getattr(self, fname)
            if isinstance(value, Model):
                value = value.id
            key.append(value)
        return tuple(key)

    @classmethod
    def cancel_move(cls, lines):
        Move = Pool().get('stock.move')
        moves = [m for l in lines for m in l.moves if l.moves]
        Move.cancel(moves)
        Move.delete(moves)

    def get_move(self):
        '''
        Return Move instance for the inventory line
        '''
        pool = Pool()
        Move = pool.get('stock.move')
        Uom = pool.get('product.uom')

        qty = self.quantity
        if qty is None:
            if self.inventory.empty_quantity is None:
                raise InventoryValidationError(
                    gettext('stock.msg_inventory_missing_empty_quantity',
                            inventory=self.inventory.rec_name))
            if self.inventory.empty_quantity == 'keep':
                return
            else:
                qty = 0.0

        delta_qty = Uom.compute_qty(self.uom, self.expected_quantity - qty,
                                    self.uom)
        if delta_qty == 0.0:
            return
        from_location = self.inventory.location
        to_location = self.inventory.location.lost_found_used
        if not to_location:
            raise InventoryValidationError(
                gettext('stock.msg_inventory_location_missing_lost_found',
                        inventory=self.inventory.rec_name,
                        location=self.inventory.location.rec_name))
        if delta_qty < 0:
            (from_location, to_location, delta_qty) = \
                (to_location, from_location, -delta_qty)

        return Move(
            from_location=from_location,
            to_location=to_location,
            quantity=delta_qty,
            product=self.product,
            uom=self.uom,
            company=self.inventory.company,
            effective_date=self.inventory.date,
            origin=self,
        )

    def update_values4complete(self, quantity):
        '''
        Return update values to complete inventory
        '''
        values = {}
        # if nothing changed, no update
        if self.expected_quantity == quantity:
            return values
        values['expected_quantity'] = quantity
        return values

    @classmethod
    def create_values4complete(cls, inventory, quantity):
        '''
        Return create values to complete inventory
        '''
        return {
            'inventory': inventory.id,
            'expected_quantity': quantity,
        }

    @classmethod
    def delete(cls, lines):
        for line in lines:
            if line.inventory_state not in {'cancelled', 'draft'}:
                raise AccessError(
                    gettext('stock.msg_inventory_line_delete_cancel',
                            line=line.rec_name,
                            inventory=line.inventory.rec_name))
        super(InventoryLine, cls).delete(lines)
Beispiel #8
0
class Line(metaclass=PoolMeta):
    __name__ = 'sale.line'

    secondary_quantity = fields.Function(fields.Float(
        "Secondary Quantity",
        digits=(16, Eval('secondary_unit_digits', 2)),
        states={
            'invisible': ((Eval('type') != 'line')
                          | ~Eval('secondary_unit')),
            'readonly': Eval('sale_state') != 'draft',
        },
        depends=[
            'secondary_unit_digits', 'type', 'secondary_unit', 'sale_state'
        ]),
                                         'on_change_with_secondary_quantity',
                                         setter='set_secondary')
    secondary_unit = fields.Many2One(
        'product.uom',
        "Secondary Unit",
        ondelete='RESTRICT',
        domain=[
            If(
                Eval('sale_state') == 'draft',
                ('category', '=', Eval('product_secondary_uom_category')), ()),
        ],
        states={
            'invisible': ((Eval('type') != 'line')
                          | (~Eval('secondary_uom_factor')
                             & ~Eval('secondary_uom_rate'))),
            'readonly':
            Eval('sale_state') != 'draft',
        },
        depends=[
            'product_secondary_uom_category', 'type', 'secondary_uom_factor',
            'secondary_uom_rate', 'sale_state'
        ])
    secondary_unit_price = fields.Function(
        fields.Numeric("Secondary Unit Price",
                       digits=price_digits,
                       states={
                           'invisible': ((Eval('type') != 'line')
                                         | ~Eval('secondary_unit')),
                           'readonly':
                           Eval('sale_state') != 'draft',
                       },
                       depends=['type', 'secondary_unit', 'sale_state']),
        'on_change_with_secondary_unit_price',
        setter='set_secondary')

    secondary_uom_factor = fields.Float("Secondary UOM Factor")
    secondary_uom_rate = fields.Float("Secondary UOM Rate")

    secondary_unit_digits = fields.Function(
        fields.Integer("Secondary Unit Digits"),
        'on_change_with_secondary_unit_digits')
    product_secondary_uom_category = fields.Function(
        fields.Many2One('product.uom.category',
                        "Product Secondary UOM Category"),
        'on_change_with_product_secondary_uom_category')

    @fields.depends('product')
    def _secondary_record(self):
        if self.product and self.product.sale_secondary_uom:
            return self.product

    @fields.depends('quantity', 'unit', 'secondary_unit',
                    'secondary_uom_factor', 'secondary_uom_rate')
    def on_change_with_secondary_quantity(self, name=None):
        pool = Pool()
        Uom = pool.get('product.uom')
        if (self.quantity and self.unit and self.secondary_unit
                and (self.secondary_uom_factor or self.secondary_uom_rate)):
            return Uom.compute_qty(self.unit,
                                   self.quantity,
                                   self.secondary_unit,
                                   round=True,
                                   factor=self.secondary_uom_factor,
                                   rate=self.secondary_uom_rate)
        else:
            return None

    @fields.depends('secondary_quantity',
                    'secondary_unit',
                    'unit',
                    'secondary_uom_factor',
                    'secondary_uom_rate',
                    methods=['on_change_quantity', 'on_change_with_amount'])
    def on_change_secondary_quantity(self):
        pool = Pool()
        Uom = pool.get('product.uom')
        if (self.secondary_quantity and self.secondary_unit and self.unit
                and (self.secondary_uom_factor or self.secondary_uom_rate)):
            self.quantity = Uom.compute_qty(self.secondary_unit,
                                            self.secondary_quantity,
                                            self.unit,
                                            round=True,
                                            factor=self.secondary_uom_rate,
                                            rate=self.secondary_uom_factor)
            self.on_change_quantity()
            self.amount = self.on_change_with_amount()

    @fields.depends('unit_price', 'unit', 'secondary_unit',
                    'secondary_uom_factor', 'secondary_uom_rate')
    def on_change_with_secondary_unit_price(self, name=None):
        pool = Pool()
        Uom = pool.get('product.uom')
        if (self.unit_price is not None and self.unit and self.secondary_unit
                and (self.secondary_uom_factor or self.secondary_uom_rate)):
            unit_price = Uom.compute_price(self.unit,
                                           self.unit_price,
                                           self.secondary_unit,
                                           factor=self.secondary_uom_factor,
                                           rate=self.secondary_uom_rate)
            return round_price(unit_price)
        else:
            return None

    @fields.depends('secondary_unit_price',
                    'secondary_unit',
                    'unit',
                    'secondary_uom_factor',
                    'secondary_uom_rate',
                    methods=['on_change_with_amount'])
    def on_change_secondary_unit_price(self, name=None):
        pool = Pool()
        Uom = pool.get('product.uom')
        if (self.secondary_unit_price is not None and self.secondary_unit
                and self.unit
                and (self.secondary_uom_factor or self.secondary_uom_rate)):
            self.unit_price = Uom.compute_price(self.secondary_unit,
                                                self.secondary_unit_price,
                                                self.unit,
                                                factor=self.secondary_uom_rate,
                                                rate=self.secondary_uom_factor)
            self.unit_price = round_price(self.unit_price)
            self.amount = self.on_change_with_amount()

    @fields.depends(methods=[
        'on_change_secondary_quantity', 'on_change_secondary_unit_price'
    ])
    def on_change_secondary_unit(self):
        self.on_change_secondary_quantity()
        self.on_change_secondary_unit_price()

    @fields.depends('secondary_unit')
    def on_change_with_secondary_unit_digits(self, name=None):
        if self.secondary_unit:
            return self.secondary_unit.digits

    @fields.depends(methods=['_secondary_record'])
    def on_change_with_product_secondary_uom_category(self, name=None):
        secondary_record = self._secondary_record()
        if secondary_record:
            return secondary_record.sale_secondary_uom.category.id

    @classmethod
    def set_secondary(cls, lines, name, value):
        pass

    @fields.depends(
        'secondary_unit',
        methods=['on_change_with_secondary_quantity', '_secondary_record'])
    def on_change_product(self):
        super().on_change_product()
        secondary_record = self._secondary_record()
        if secondary_record and self.secondary_unit:
            secondary_uom = secondary_record.sale_secondary_uom
            if secondary_uom:
                if self.secondary_unit.category != secondary_uom.category:
                    self.secondary_unit = None

        if secondary_record:
            self.secondary_uom_factor = (
                secondary_record.sale_secondary_uom_normal_factor)
            self.secondary_uom_rate = (
                secondary_record.sale_secondary_uom_normal_rate)
        else:
            self.secondary_unit = None
            self.secondary_uom_factor = None
            self.secondary_uom_rate = None
        self.secondary_quantity = self.on_change_with_secondary_quantity()

    def get_invoice_line(self):
        pool = Pool()
        InvoiceLine = pool.get('account.invoice.line')
        lines = super().get_invoice_line()
        if hasattr(InvoiceLine, 'secondary_unit'):
            for line in lines:
                if line.type != 'line':
                    continue
                if line.unit == self.unit:
                    line.secondary_unit = self.secondary_unit
        return lines

    def get_move(self, shipment_type):
        move = super().get_move(shipment_type)
        if move and hasattr(move.__class__, 'secondary_unit'):
            if move.uom == self.unit:
                move.secondary_unit = self.secondary_unit
        return move
Beispiel #9
0
class BudgetLine(ModelSQL, ModelView):
    'General Budget Lines'
    _name = 'ekd.account.budget.line'
    _description = __doc__
    __sequence_line = 0

    budget = fields.Many2One('ekd.account.budget',
                             'Budget',
                             ondelete="RESTRICT")
    direct_line = fields.Selection([('income', 'Income'),
                                    ('expense', 'expense'),
                                    ('total_sum', 'Total Summa'),
                                    ('total_per', 'Total Percentage')],
                                   'Direct Budget',
                                   required=True)
    income = fields.Boolean('Income Budget')
    kind = fields.Selection([('quantity', 'Quantity'), ('amount', 'Amount'),
                             ('quan_amount', 'Quantity and Amount'),
                             ('percentage', 'Percentage')], 'Kind')
    type_line = fields.Selection([('section', 'Section'),
                                  ('subtotal', 'Subtotal'), ('line', 'Line')],
                                 'Type line')
    analytic = fields.Many2One('ekd.account.analytic',
                               'Analytic Account',
                               domain=[('kind_analytic', '=',
                                        Eval('direct_line'))],
                               ondelete="RESTRICT")
    code_section = fields.Char('Budget Section',
                               size=None,
                               help="Code Section")
    code = fields.Char('Budget Item', size=None, help="Code Item")
    name = fields.Char('Name',
                       size=None,
                       required=True,
                       on_change_with=['analytic', 'name'])
    sequence = fields.Char('Code Line', size=None, required=True)
    parent = fields.Many2One('ekd.account.budget.line',
                             'Parent',
                             ondelete="RESTRICT")
    #            , domain=[('budget', '=', budget.id)])
    childs = fields.One2Many('ekd.account.budget.line', 'parent', 'Children')
    product = fields.Many2One('product.product', 'Product')
    quantity = fields.Float('Quantity',
                            digits=(16, Eval('unit_digits', 2)),
                            on_change=['quantity', 'price_unit'],
                            states={
                                'invisible':
                                Or(Equal(Eval('kind'), 'amount'),
                                   Not(Equal(Eval('type_line'), 'line'))),
                                'tree_invisible':
                                Or(Equal(Eval('kind'), 'amount'),
                                   Not(Equal(Eval('type_line'), 'line')))
                            },
                            depends=['kind', 'type_line'])
    uom = fields.Many2One('product.uom',
                          'Unit',
                          states={
                              'invisible':
                              Or(Equal(Eval('kind'), 'amount'),
                                 Not(Equal(Eval('type_line'), 'line'))),
                              'tree_invisible':
                              Or(Equal(Eval('kind'), 'amount'),
                                 Not(Equal(Eval('type_line'), 'line')))
                          },
                          depends=['kind', 'type_line'])
    unit_digits = fields.Function(
        fields.Integer('Unit Digits', on_change_with=['uom']),
        'get_unit_digits')
    price_unit = fields.Numeric('Price unit',
                                digits=(16, 2),
                                states={
                                    'invisible':
                                    Or(Equal(Eval('kind'), 'amount'),
                                       Not(Equal(Eval('type_line'), 'line'))),
                                    'tree_invisible':
                                    Or(Equal(Eval('kind'), 'amount'),
                                       Equal(Eval('type_line'), 'line'))
                                },
                                depends=['kind', 'type_line'],
                                on_change=['quantity', 'price_unit'])
    amount = fields.Numeric(
        'Amount',
        digits=(16, 2),
        states={'readonly': Not(Equal(Eval('state'), 'draft'))})
    amount_change = fields.Function(
        fields.Numeric('Amount Change',
                       digits=(16, 2),
                       states={
                           'invisible': Equal(Eval('state'), 'draft'),
                           'tree_invisible': Equal(Eval('state'), 'draft')
                       }), 'get_amount_change')
    amount_move = fields.Function(
        fields.Numeric('Amount Accounting',
                       digits=(16, 2),
                       states={
                           'invisible': Equal(Eval('state'), 'draft'),
                           'tree_invisible': Equal(Eval('state'), 'draft')
                       }), 'get_amount_move')
    quantity_move = fields.Function(
        fields.Numeric('Quantity Accounting',
                       digits=(16, 2),
                       states={
                           'invisible': Equal(Eval('state'), 'draft'),
                           'tree_invisible': Equal(Eval('state'), 'draft')
                       }), 'get_amount_move')
    currency = fields.Many2One(
        'currency.currency',
        'Currency',
        states={'invisible': Equal(Eval('kind'), 'quantity')},
        depends=['kind'])
    line_change = fields.One2Many('ekd.account.budget.line.change',
                                  'budget_line',
                                  'Budget Line Change',
                                  states={
                                      'invisible':
                                      Equal(Eval('state'), 'draft'),
                                      'readonly':
                                      Equal(Eval('state'), 'draft'),
                                  },
                                  context={
                                      'budget': Eval('budget'),
                                      'name': Eval('name'),
                                      'uom': Eval('uom'),
                                      'analityc': Eval('analytic'),
                                      'quantity': Eval('quantity'),
                                      'unit_digits': Eval('unit_digits'),
                                      'price_unit': Eval('price_unit'),
                                      'amount': Eval('amount'),
                                  })
    move_line = fields.One2Many('ekd.account.move.line.budget',
                                'budget_line',
                                'Amount Accounting',
                                states={
                                    'invisible': Equal(Eval('state'), 'draft'),
                                    'readonly': Equal(Eval('state'), 'draft'),
                                })
    state = fields.Selection([('draft', 'Draft'), ('running', 'Performed'),
                              ('done', 'Completed'), ('deleted', 'Deleted')],
                             'State',
                             required=True,
                             readonly=True)

    def __init__(self):
        super(BudgetLine, self).__init__()
        self._sql_constraints += [
            ('code_uniq', 'UNIQUE(budget,sequence)',
             'The sequence in the budget must be unique!'),
        ]
        self._order.insert(0, ('budget', 'ASC'))
        self._order.insert(0, ('sequence', 'ASC'))
        self._order.insert(1, ('analytic', 'ASC'))

        self._rpc.update({
            'on_write': True,
        })

    def default_type_line(self):
        return 'line'

    def default_state(self):
        return 'draft'

    def default_sequence(self):
        self.__sequence_line = self.__sequence_line + 10
        if self.__sequence_line < 100:
            return "00%s" % (self.__sequence_line)
        elif self.__sequence_line < 1000:
            return "0%s" % (self.__sequence_line)
        else:
            return str(self.__sequence_line)

    def default_kind(self):
        return 'quan_amount'

    def default_direct_line(self):
        return 'expense'

    def default_type_line(self):
        return 'line'

    def default_income(self):
        return False

    def get_unit_digits(self, ids, name):
        res = {}
        for line in self.browse(ids):
            if line.uom:
                res[line.id] = line.uom.digits
            else:
                res[line.id] = 2
        return res

    def get_amount_change(self, ids, name):
        res = {}.fromkeys(ids, Decimal('0.0'))
        for line in self.browse(ids):
            if line.line_change:
                for line_change in line.line_change:
                    res[line.id] += line_change.amount
            res[line.id] += line.amount
        return res

    def get_amount_move(self, ids, names):
        res = {}
        for name in names:
            res.setdefault(name, {})
        for line in self.browse(ids):
            if name == 'amount_move':
                res[name][line.id] = Decimal('0.0')
            if name == 'quantity_move':
                res[name][line.id] = 0.0
            if line.move_line:
                for move_line in line.move_line:
                    res[name][line.id] += move_line.amount
        return res

    def on_write(self, ids):
        lines = self.browse(ids)
        res = []
        for line in lines:
            res.extend([x.id for x in line.move.lines])


#        raise Exception(str(res), ids, str(context))
        return list({}.fromkeys(res))

    def on_change_with_name(self, vals):
        if not vals.get('analytic'):
            return
        analytic_obj = self.pool.get('ekd.account.analytic')
        if not vals.get('name'):
            return analytic_obj.browse(vals.get('analytic')).rec_name

    def on_change_with_unit_digits(self, vals):
        if not vals.get('uom'):
            return 2
        uom_obj = self.pool.get('product.uom')
        return uom_obj.browse(vals.get('uom')).digits

    def on_change_with_amount(self, vals):
        if not vals.get('quantity') or not vals.get('price_unit'):
            return vals
        return Decimal(str(vals.get('quantity'))) * Decimal(
            str(vals.get('price_unit')))

    def on_change_quantity(self, vals):
        if not vals.get('quantity') or not vals.get('price_unit'):
            return vals
        return {
            'amount':
            Decimal(str(vals.get('quantity'))) *
            Decimal(str(vals.get('price_unit')))
        }

    def on_change_price_unit(self, vals):
        if not vals.get('quantity') or not vals.get('price_unit'):
            return vals
        return {
            'amount':
            Decimal(str(vals.get('quantity'))) *
            Decimal(str(vals.get('price_unit')))
        }

    def delete(self, ids):
        return super(BudgetLine, self).delete(ids)

    def create(self, vals):
        return super(BudgetLine, self).create(vals)

    def write(self, ids, vals):
        return super(BudgetLine, self).write(ids, vals)
Beispiel #10
0
class AmendmentLine(ModelSQL, ModelView):
    "Sale Amendment Line"
    __name__ = 'sale.amendment.line'

    amendment = fields.Many2One('sale.amendment', "Amendment", required=True)
    state = fields.Function(fields.Selection('get_states', "State"),
                            'on_change_with_state')
    action = fields.Selection([
        ('taxes', "Recompute Taxes"),
        ('payment_term', "Change Payment Term"),
        ('party', "Change Parties and Addresses"),
        ('warehouse', "Change Warehouse"),
        ('line', "Change Line"),
    ],
                              "Action",
                              required=True,
                              states={
                                  'readonly': Eval('state') != 'draft',
                              },
                              depends=['state'])

    sale = fields.Function(fields.Many2One('sale.sale', "Sale"),
                           'on_change_with_sale')
    line = fields.Many2One('sale.line',
                           "Line",
                           domain=[
                               ('type', '=', 'line'),
                               ('sale', '=', Eval('sale', -1)),
                           ],
                           states={
                               'readonly': Eval('state') != 'draft',
                               'invisible': Eval('action') != 'line',
                               'required': Eval('action') == 'line',
                           },
                           depends=['state', 'sale', 'action'])

    payment_term = fields.Many2One('account.invoice.payment_term',
                                   "Payment Term",
                                   states={
                                       'invisible':
                                       Eval('action') != 'payment_term',
                                   },
                                   depends=['action'])

    party = fields.Many2One('party.party',
                            "Party",
                            states={
                                'readonly': Eval('state') != 'draft',
                                'invisible': Eval('action') != 'party',
                                'required': Eval('action') == 'party',
                            },
                            depends=['state', 'action'])
    invoice_address = fields.Many2One('party.address',
                                      "Invoice Address",
                                      domain=[
                                          ('party', '=', Eval('party')),
                                      ],
                                      states={
                                          'readonly': Eval('state') != 'draft',
                                          'invisible':
                                          Eval('action') != 'party',
                                          'required':
                                          Eval('action') == 'party',
                                      },
                                      depends=['state', 'action', 'party'])
    shipment_party = fields.Many2One('party.address',
                                     "Shipment Party",
                                     states={
                                         'readonly': Eval('state') != 'draft',
                                         'invisible':
                                         Eval('action') != 'party',
                                     },
                                     depends=['state', 'action'])
    shipment_address = fields.Many2One(
        'party.address',
        "Shipment Address",
        domain=[
            ('party', '=',
             If(Eval('shipment_party'), Eval('shipment_party'),
                Eval('party'))),
        ],
        states={
            'readonly': Eval('state') != 'draft',
            'invisible': Eval('action') != 'party',
            'required': Eval('action') == 'party',
        },
        depends=['state', 'action', 'party', 'shipment_party'])

    warehouse = fields.Many2One('stock.location',
                                "Warehouse",
                                domain=[
                                    ('type', '=', 'warehouse'),
                                ],
                                states={
                                    'readonly': Eval('state') != 'draft',
                                    'invisible': Eval('action') != 'warehouse',
                                    'required': Eval('action') == 'warehouse',
                                },
                                depends=['state', 'action'])

    product = fields.Many2One(
        'product.product',
        "Product",
        domain=[
            If(Eval('state') == 'draft', ('salable', '=', True), ()),
            ('default_uom_category', '=', Eval('product_uom_category')),
        ],
        states={
            'readonly': Eval('state') != 'draft',
            'invisible': Eval('action') != 'line',
            'required': Eval('action') == 'line',
        },
        depends=['action', 'state', 'product_uom_category'])
    quantity = fields.Float("Quantity",
                            digits=(16, Eval('unit_digits', 2)),
                            states={
                                'readonly': Eval('state') != 'draft',
                                'invisible': Eval('action') != 'line',
                                'required': Eval('action') == 'line',
                            },
                            depends=['unit_digits', 'action'])
    unit = fields.Many2One('product.uom',
                           "Unit",
                           ondelete='RESTRICT',
                           domain=[
                               ('category', '=', Eval('product_uom_category')),
                           ],
                           states={
                               'readonly': Eval('state') != 'draft',
                               'invisible': Eval('action') != 'line',
                               'required': Eval('action') == 'line',
                           },
                           depends=['state', 'product_uom_category', 'action'])
    unit_price = fields.Numeric("Unit Price",
                                digits=price_digits,
                                states={
                                    'readonly': Eval('state') != 'draft',
                                    'invisible': Eval('action') != 'line',
                                    'required': Eval('action') == 'line',
                                },
                                depends=['state', 'action'])
    description = fields.Text("Description",
                              states={
                                  'readonly': Eval('state') != 'draft',
                                  'invisible': Eval('action') != 'line',
                              },
                              depends=['state', 'action'])

    unit_digits = fields.Function(fields.Integer("Unit Digits"),
                                  'on_change_with_unit_digits')
    product_uom_category = fields.Function(
        fields.Many2One('product.uom.category', "Product UoM Category"),
        'on_change_with_product_uom_category')

    @fields.depends('amendment', '_parent_amendment.sale',
                    '_parent_amendment._parent_sale.payment_term',
                    '_parent_amendment._parent_sale.party',
                    '_parent_amendment._parent_sale.invoice_address',
                    '_parent_amendment._parent_sale.shipment_party',
                    '_parent_amendment._parent_sale.shipment_address',
                    '_parent_amendment._parent_sale.warehouse')
    def on_change_amendment(self):
        if self.amendment and self.amendment.sale:
            self.payment_term = self.amendment.sale.payment_term

            self.party = self.amendment.sale.party
            self.invoice_address = self.amendment.sale.invoice_address
            self.shipment_party = self.amendment.sale.shipment_party
            self.shipment_address = self.amendment.sale.shipment_address

            self.warehouse = self.amendment.sale.warehouse

    @fields.depends(methods=['on_change_amendment'])
    def on_change_state(self):
        self.on_change_amendment()

    @classmethod
    def get_states(cls):
        pool = Pool()
        Amendment = pool.get('sale.amendment')
        return Amendment.fields_get(['state'])['state']['selection']

    @fields.depends('amendment', '_parent_amendment.state')
    def on_change_with_state(self, name=None):
        if self.amendment:
            return self.amendment.state

    @fields.depends(methods=['on_change_line', 'on_change_amendment'])
    def on_change_action(self):
        self.line = None
        self.on_change_line()
        self.on_change_amendment()

    @fields.depends('amendment', '_parent_amendment.sale')
    def on_change_with_sale(self, name=None):
        if self.amendment and self.amendment.sale:
            return self.amendment.sale.id

    @fields.depends('line')
    def on_change_line(self):
        if self.line:
            self.product = self.line.product
            self.quantity = self.line.quantity
            self.unit = self.line.unit
            self.unit_price = self.line.unit_price
        else:
            self.product = self.quantity = self.unit = self.unit_price = None

    @fields.depends('party', 'shipment_party')
    def on_change_party(self):
        self.invoice_address = None
        if not self.shipment_party:
            self.shipment_address = None
        if self.party:
            self.invoice_address = self.party.address_get(type='invoice')
            if not self.shipment_party:
                self.shipment_address = self.party.address_get(type='delivery')

    @fields.depends('party', 'shipment_party')
    def on_change_shipment_party(self):
        if self.shipment_party:
            self.shipment_address = self.shipment_party.address_get(
                type='delivery')
        elif self.party:
            self.shipment_address = self.party.address_get(type='delivery')

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

    @fields.depends('line')
    def on_change_with_product_uom_category(self, name=None):
        if self.line:
            return self.line.product_uom_category.id

    def apply(self, sale):
        assert self.sale == sale
        sale_line = None
        if self.line:
            for line in sale.lines:
                if self.line == line:
                    sale_line = line
                    break
        getattr(self, '_apply_%s' % self.action)(sale, sale_line)

    def _apply_taxes(self, sale, sale_line):
        for line in sale.lines:
            line.taxes = line.compute_taxes(sale.party)

    def _apply_payment_term(self, sale, sale_line):
        sale.payment_term = self.payment_term

    def _apply_party(self, sale, sale_line):
        sale.party = self.party
        sale.invoice_address = self.invoice_address
        sale.shipment_party = self.shipment_party
        sale.shipment_address = self.shipment_address

    def _apply_warehouse(self, sale, sale_line):
        sale.warehouse = self.warehouse

    def _apply_line(self, sale, sale_line):
        sale_line.product = self.product
        sale_line.quantity = self.quantity
        sale_line.unit = self.unit
        sale_line.unit_price = self.unit_price
        sale_line.description = self.description
Beispiel #11
0
class Asset(Workflow, ModelSQL, ModelView):
    'Asset'
    __name__ = 'account.asset'
    _rec_name = 'number'
    number = fields.Char('Number', readonly=True, select=True)
    product = fields.Many2One('product.product',
                              'Product',
                              required=True,
                              states={
                                  'readonly': (Eval('lines', [0]) |
                                               (Eval('state') != 'draft')),
                              },
                              depends=['state'],
                              domain=[
                                  ('type', '=', 'assets'),
                                  ('depreciable', '=', True),
                              ])
    supplier_invoice_line = fields.Many2One(
        'account.invoice.line',
        'Supplier Invoice Line',
        domain=[
            If(
                Eval('product', None) == None,
                ('product', '=', -1),
                ('product', '=', Eval('product', -1)),
            ),
            ('invoice.type', '=', 'in'),
            [
                'OR',
                ('company', '=', Eval('company', -1)),
                ('invoice.company', '=', Eval('company', -1)),
            ],
        ],
        states={
            'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')),
        },
        depends=['product', 'state', 'company'])
    customer_invoice_line = fields.Function(
        fields.Many2One('account.invoice.line', 'Customer Invoice Line'),
        'get_customer_invoice_line')
    account_journal = fields.Many2One('account.journal',
                                      'Journal',
                                      states={
                                          'readonly': Eval('state') != 'draft',
                                      },
                                      depends=['state'],
                                      domain=[('type', '=', 'asset')],
                                      required=True)
    company = fields.Many2One('company.company',
                              'Company',
                              states={
                                  'readonly': Eval('state') != 'draft',
                              },
                              depends=['state'],
                              required=True)
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
                                      'on_change_with_currency_digits')
    quantity = fields.Float('Quantity',
                            digits=(16, Eval('unit_digits', 2)),
                            states={
                                'readonly':
                                (Bool(Eval('supplier_invoice_line', 1))
                                 | Eval('lines', [0])
                                 | (Eval('state') != 'draft')),
                            },
                            depends=['state', 'unit_digits'])
    unit = fields.Many2One('product.uom',
                           'Unit',
                           states={
                               'readonly': (Bool(Eval('product'))
                                            | (Eval('state') != 'draft')),
                           },
                           depends=['state'])
    unit_digits = fields.Function(fields.Integer('Unit Digits'),
                                  'on_change_with_unit_digits')
    value = fields.Numeric('Value',
                           digits=(16, Eval('currency_digits', 2)),
                           states={
                               'readonly': (Eval('lines', [0]) |
                                            (Eval('state') != 'draft')),
                           },
                           depends=['currency_digits', 'state'],
                           required=True)
    residual_value = fields.Numeric(
        'Residual Value',
        states={
            'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')),
        },
        depends=['currency_digits', 'state'],
        required=True,
        digits=(16, Eval('currency_digits', 2)))
    purchase_date = fields.Date('Purchase Date',
                                states={
                                    'readonly':
                                    (Bool(Eval('supplier_invoice_line', 1))
                                     | Eval('lines', [0])
                                     | (Eval('state') != 'draft')),
                                },
                                required=True,
                                depends=['state'])
    start_date = fields.Date(
        'Start Date',
        states={
            'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')),
        },
        required=True,
        domain=[('start_date', '<=', Eval('end_date', None))],
        depends=['state', 'end_date'])
    end_date = fields.Date(
        'End Date',
        states={
            'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')),
        },
        required=True,
        domain=[('end_date', '>=', Eval('start_date', None))],
        depends=['state', 'start_date'])
    depreciation_method = fields.Selection(
        [
            ('linear', 'Linear'),
        ],
        'Depreciation Method',
        states={
            'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')),
        },
        required=True,
        depends=['state'])
    frequency = fields.Selection([
        ('monthly', 'Monthly'),
        ('yearly', 'Yearly'),
    ],
                                 'Frequency',
                                 required=True,
                                 states={
                                     'readonly': (Eval('lines', [0]) |
                                                  (Eval('state') != 'draft')),
                                 },
                                 depends=['state'])
    state = fields.Selection([
        ('draft', 'Draft'),
        ('running', 'Running'),
        ('closed', 'Closed'),
    ],
                             'State',
                             readonly=True)
    lines = fields.One2Many('account.asset.line',
                            'asset',
                            'Lines',
                            readonly=True)
    move = fields.Many2One('account.move',
                           'Account Move',
                           readonly=True,
                           domain=[
                               ('company', '=', Eval('company', -1)),
                           ],
                           depends=['company'])
    update_moves = fields.Many2Many('account.asset-update-account.move',
                                    'asset',
                                    'move',
                                    'Update Moves',
                                    readonly=True,
                                    domain=[
                                        ('company', '=', Eval('company', -1)),
                                    ],
                                    states={
                                        'invisible': ~Eval('update_moves'),
                                    },
                                    depends=['company'])
    comment = fields.Text('Comment')

    @classmethod
    def __setup__(cls):
        super(Asset, cls).__setup__()
        table = cls.__table__()
        cls._sql_constraints = [
            ('invoice_line_uniq', Unique(table, table.supplier_invoice_line),
             'Supplier Invoice Line can be used only once on asset.'),
        ]
        cls._error_messages.update({
            'delete_draft':
            'Asset "%s" must be in draft to be deleted.',
        })
        cls._transitions |= set((
            ('draft', 'running'),
            ('running', 'closed'),
        ))
        cls._buttons.update({
            'run': {
                'invisible': Eval('state') != 'draft',
            },
            'close': {
                'invisible': Eval('state') != 'running',
            },
            'create_lines': {
                'invisible': (Eval('lines', [])
                              | (Eval('state') != 'draft')),
            },
            'clear_lines': {
                'invisible': (~Eval('lines', [0])
                              | (Eval('state') != 'draft')),
            },
            'update': {
                'invisible': Eval('state') != 'running',
            },
        })

    @classmethod
    def __register__(cls, module_name):
        TableHandler = backend.get('TableHandler')
        table_h = TableHandler(cls, module_name)

        # Migration from 3.8: rename reference into number
        if table_h.column_exist('reference'):
            table_h.column_rename('reference', 'number')
        super(Asset, cls).__register__(module_name)

    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def default_frequency():
        return 'monthly'

    @staticmethod
    def default_depreciation_method():
        return 'linear'

    @staticmethod
    def default_start_date():
        return Pool().get('ir.date').today()

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

    @staticmethod
    def default_account_journal():
        Journal = Pool().get('account.journal')
        journals = Journal.search([
            ('type', '=', 'asset'),
        ])
        if len(journals) == 1:
            return journals[0].id
        return None

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

    @fields.depends('supplier_invoice_line', 'unit')
    def on_change_supplier_invoice_line(self):
        pool = Pool()
        Currency = pool.get('currency.currency')
        Unit = Pool().get('product.uom')

        if not self.supplier_invoice_line:
            self.quantity = None
            self.value = None
            self.start_date = self.default_start_date()
            return

        invoice_line = self.supplier_invoice_line
        invoice = invoice_line.invoice
        if invoice.company.currency != invoice.currency:
            with Transaction().set_context(date=invoice.currency_date):
                self.value = Currency.compute(invoice.currency,
                                              invoice_line.amount,
                                              invoice.company.currency)
        else:
            self.value = invoice_line.amount
        if invoice.invoice_date:
            self.purchase_date = invoice.invoice_date
            self.start_date = invoice.invoice_date
            if invoice_line.product.depreciation_duration:
                duration = relativedelta.relativedelta(months=int(
                    invoice_line.product.depreciation_duration),
                                                       days=-1)
                self.end_date = self.start_date + duration

        if not self.unit:
            self.quantity = invoice_line.quantity
        else:
            self.quantity = Unit.compute_qty(invoice_line.unit,
                                             invoice_line.quantity, self.unit)

    @fields.depends('product')
    def on_change_with_unit(self):
        if not self.product:
            return None
        return self.product.default_uom.id

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

    @fields.depends('end_date', 'product', 'start_date')
    def on_change_with_end_date(self):
        if all(getattr(self, k, None) for k in ('product', 'start_date')):
            if self.product.depreciation_duration:
                duration = relativedelta.relativedelta(months=int(
                    self.product.depreciation_duration),
                                                       days=-1)
                return self.start_date + duration
        return self.end_date

    @classmethod
    def get_customer_invoice_line(cls, assets, name):
        InvoiceLine = Pool().get('account.invoice.line')
        invoice_lines = InvoiceLine.search([
            ('asset', 'in', [a.id for a in assets]),
        ])
        result = dict((a.id, None) for a in assets)
        result.update(dict((l.asset.id, l.id) for l in invoice_lines))
        return result

    def get_depreciated_amount(self):
        lines = [
            line.accumulated_depreciation for line in self.lines
            if line.move and line.move.state == 'posted'
        ]
        return max(lines) if lines else 0

    def compute_move_dates(self):
        """
        Returns all the remaining dates at which asset depreciation movement
        will be issued.
        """
        start_date = max([self.start_date] + [l.date for l in self.lines])
        delta = relativedelta.relativedelta(self.end_date, start_date)
        # dateutil >= 2.0 has replace __nonzero__ by __bool__ which doesn't
        # work in Python < 3
        if delta == relativedelta.relativedelta():
            return [self.end_date]
        if self.frequency == 'monthly':
            rule = rrule.rrule(rrule.MONTHLY,
                               dtstart=self.start_date,
                               bymonthday=-1)
        elif self.frequency == 'yearly':
            rule = rrule.rrule(rrule.YEARLY,
                               dtstart=self.start_date,
                               bymonth=12,
                               bymonthday=-1)
        dates = [
            d.date() for d in rule.between(date2datetime(start_date),
                                           date2datetime(self.end_date))
        ]
        dates.append(self.end_date)
        return dates

    def compute_depreciation(self, date, dates):
        """
        Returns the depreciation amount for an asset on a certain date.
        """
        amount = (self.value - self.get_depreciated_amount() -
                  self.residual_value)
        if self.depreciation_method == 'linear':
            start_date = max(
                [self.start_date - relativedelta.relativedelta(days=1)] +
                [l.date for l in self.lines])
            first_delta = dates[0] - start_date
            if len(dates) > 1:
                last_delta = dates[-1] - dates[-2]
            else:
                last_delta = first_delta
            if self.frequency == 'monthly':
                _, first_ndays = calendar.monthrange(dates[0].year,
                                                     dates[0].month)
                _, last_ndays = calendar.monthrange(dates[-1].year,
                                                    dates[-1].month)
            elif self.frequency == 'yearly':
                first_ndays = 365 + calendar.isleap(dates[0].year)
                last_ndays = 365 + calendar.isleap(dates[-1].year)
            first_ratio = Decimal(first_delta.days) / Decimal(first_ndays)
            last_ratio = Decimal(last_delta.days) / Decimal(last_ndays)
            depreciation = amount / (len(dates) - 2 + first_ratio + last_ratio)
            if date == dates[0]:
                depreciation *= first_ratio
            elif date == dates[-1]:
                depreciation *= last_ratio
            return self.company.currency.round(depreciation)

    def depreciate(self):
        """
        Returns all the depreciation amounts still to be accounted.
        """
        Line = Pool().get('account.asset.line')
        amounts = {}
        dates = self.compute_move_dates()
        amount = (self.value - self.get_depreciated_amount() -
                  self.residual_value)
        if amount <= 0:
            return amounts
        residual_value, acc_depreciation = amount, Decimal(0)
        asset_line = None
        for date in dates:
            depreciation = self.compute_depreciation(date, dates)
            amounts[date] = asset_line = Line(
                acquired_value=self.value,
                depreciable_basis=amount,
            )
            if depreciation > residual_value:
                asset_line.depreciation = residual_value
                asset_line.accumulated_depreciation = (
                    self.get_depreciated_amount() + acc_depreciation +
                    residual_value)
                break
            else:
                residual_value -= depreciation
                acc_depreciation += depreciation
                asset_line.depreciation = depreciation
                asset_line.accumulated_depreciation = (
                    self.get_depreciated_amount() + acc_depreciation)
        else:
            if residual_value > 0 and asset_line is not None:
                asset_line.depreciation += residual_value
                asset_line.accumulated_depreciation += residual_value
        for asset_line in amounts.itervalues():
            asset_line.actual_value = (self.value -
                                       asset_line.accumulated_depreciation)
        return amounts

    @classmethod
    @ModelView.button
    def create_lines(cls, assets):
        cls.clear_lines(assets)

        for asset in assets:
            for date, line in asset.depreciate().iteritems():
                line.asset = asset.id
                line.date = date
                line.save()

    @classmethod
    @ModelView.button
    def clear_lines(cls, assets):
        Line = Pool().get('account.asset.line')

        lines_to_delete = []
        for asset in assets:
            for line in asset.lines:
                if not line.move or line.move.state != 'posted':
                    lines_to_delete.append(line)
        Line.delete(lines_to_delete)

    @classmethod
    @ModelView.button_action('account_asset.wizard_update')
    def update(cls, assets):
        pass

    def get_move(self, line):
        """
        Return the account.move generated by an asset line.
        """
        pool = Pool()
        Period = pool.get('account.period')
        Move = pool.get('account.move')
        MoveLine = pool.get('account.move.line')

        period_id = Period.find(self.company.id, line.date)
        expense_line = MoveLine(
            credit=0,
            debit=line.depreciation,
            account=self.product.account_expense_used,
        )
        depreciation_line = MoveLine(
            debit=0,
            credit=line.depreciation,
            account=self.product.account_depreciation_used,
        )

        return Move(
            company=self.company,
            origin=line,
            period=period_id,
            journal=self.account_journal,
            date=line.date,
            lines=[expense_line, depreciation_line],
        )

    @classmethod
    def create_moves(cls, assets, date):
        """
        Creates all account move on assets before a date.
        """
        pool = Pool()
        Move = pool.get('account.move')
        Line = pool.get('account.asset.line')

        moves = []
        lines = []
        for asset_ids in grouped_slice(assets):
            lines += Line.search([
                ('asset', 'in', list(asset_ids)),
                ('date', '<=', date),
                ('move', '=', None),
            ])
        for line in lines:
            move = line.asset.get_move(line)
            move.save()
            moves.append(move)
            Line.write([line], {
                'move': move.id,
            })
        Move.post(moves)

    def get_closing_move(self, account):
        """
        Returns closing move values.
        """
        pool = Pool()
        Period = pool.get('account.period')
        Date = pool.get('ir.date')
        Move = pool.get('account.move')
        MoveLine = pool.get('account.move.line')

        date = Date.today()
        period_id = Period.find(self.company.id, date)
        if self.supplier_invoice_line:
            account_asset = self.supplier_invoice_line.account
        else:
            account_asset = self.product.account_asset_used

        asset_line = MoveLine(
            debit=0,
            credit=self.value,
            account=account_asset,
        )
        depreciation_line = MoveLine(
            debit=self.get_depreciated_amount(),
            credit=0,
            account=self.product.account_depreciation_used,
        )
        lines = [asset_line, depreciation_line]
        square_amount = asset_line.credit - depreciation_line.debit
        if square_amount:
            if not account:
                account = self.product.account_revenue_used
            counter_part_line = MoveLine(
                debit=square_amount if square_amount > 0 else 0,
                credit=-square_amount if square_amount < 0 else 0,
                account=account,
            )
            lines.append(counter_part_line)
        return Move(
            company=self.company,
            origin=self,
            period=period_id,
            journal=self.account_journal,
            date=date,
            lines=lines,
        )

    @classmethod
    def set_number(cls, assets):
        '''
        Fill the number field with asset sequence.
        '''
        pool = Pool()
        Sequence = pool.get('ir.sequence')
        Config = pool.get('account.configuration')

        config = Config(1)
        for asset in assets:
            if asset.number:
                continue
            asset.number = Sequence.get_id(config.asset_sequence.id)
        cls.save(assets)

    @classmethod
    @ModelView.button
    @Workflow.transition('running')
    def run(cls, assets):
        cls.set_number(assets)
        cls.create_lines(assets)

    @classmethod
    @ModelView.button
    @Workflow.transition('closed')
    def close(cls, assets, account=None):
        """
        Close the assets.
        If account is provided, it will be used instead of the expense account.
        """
        Move = Pool().get('account.move')

        cls.clear_lines(assets)
        moves = []
        for asset in assets:
            move = asset.get_closing_move(account)
            move.save()
            moves.append(move)
            cls.write([asset], {
                'move': move.id,
            })
        Move.post(moves)

    def get_rec_name(self, name):
        return '%s - %s' % (self.number, self.product.rec_name)

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

    @classmethod
    def copy(cls, assets, default=None):
        if default is None:
            default = {}
        default = default.copy()
        default.setdefault('lines', [])
        default.setdefault('state', 'draft')
        default.setdefault('number', None)
        default.setdefault('supplier_invoice_line', None)
        return super(Asset, cls).copy(assets, default=default)

    @classmethod
    def delete(cls, assets):
        for asset in assets:
            if asset.state != 'draft':
                cls.raise_user_error('delete_draft', asset.rec_name)
        return super(Asset, cls).delete(assets)
Beispiel #12
0
class InpatientMedication(ModelSQL, ModelView):
    'Inpatient Medication'
    __name__ = 'gnuhealth.inpatient.medication'

    name = fields.Many2One('gnuhealth.inpatient.registration',
                           'Registration Code')
    medicament = fields.Many2One('gnuhealth.medicament',
                                 'Medicament',
                                 required=True,
                                 help='Prescribed Medicament')
    indication = fields.Many2One(
        'gnuhealth.pathology',
        'Indication',
        help='Choose a disease for this medicament from the disease list. It'
        ' can be an existing disease of the patient or a prophylactic.')
    start_treatment = fields.DateTime('Start',
                                      help='Date of start of Treatment',
                                      required=True)
    end_treatment = fields.DateTime('End', help='Date of start of Treatment')
    dose = fields.Float('Dose',
                        help='Amount of medication (eg, 250 mg) per dose',
                        required=True)
    dose_unit = fields.Many2One(
        'gnuhealth.dose.unit',
        'dose unit',
        required=True,
        help='Unit of measure for the medication to be taken')
    route = fields.Many2One('gnuhealth.drug.route',
                            'Administration Route',
                            required=True,
                            help='Drug administration route code.')
    form = fields.Many2One('gnuhealth.drug.form',
                           'Form',
                           required=True,
                           help='Drug form, such as tablet or gel')
    qty = fields.Integer(
        'x',
        required=True,
        help='Quantity of units (eg, 2 capsules) of the medicament')
    common_dosage = fields.Many2One(
        'gnuhealth.medication.dosage',
        'Frequency',
        help='Common / standard dosage frequency for this medicament')
    admin_times = fields.One2Many('gnuhealth.inpatient.medication.admin_time',
                                  'name', "Admin times")
    log_history = fields.One2Many('gnuhealth.inpatient.medication.log', 'name',
                                  "Log History")
    frequency = fields.Integer(
        'Frequency',
        help='Time in between doses the patient must wait (ie, for 1 pill'
        ' each 8 hours, put here 8 and select \"hours\" in the unit field')
    frequency_unit = fields.Selection([
        (None, ''),
        ('seconds', 'seconds'),
        ('minutes', 'minutes'),
        ('hours', 'hours'),
        ('days', 'days'),
        ('weeks', 'weeks'),
        ('wr', 'when required'),
    ],
                                      'unit',
                                      select=True,
                                      sort=False)
    frequency_prn = fields.Boolean('PRN', help='Use it as needed, pro re nata')
    is_active = fields.Boolean(
        'Active',
        help='Check if the patient is currently taking the medication')
    discontinued = fields.Boolean('Discontinued')
    course_completed = fields.Boolean('Course Completed')
    discontinued_reason = fields.Char(
        'Reason for discontinuation',
        states={
            'invisible': Not(Bool(Eval('discontinued'))),
            'required': Bool(Eval('discontinued')),
        },
        depends=['discontinued'],
        help='Short description for discontinuing the treatment')
    adverse_reaction = fields.Text(
        'Adverse Reactions',
        help='Side effects or adverse reactions that the patient experienced')

    @fields.depends('discontinued', 'course_completed')
    def on_change_with_is_active(self):
        is_active = True
        if (self.discontinued or self.course_completed):
            is_active = False
        return is_active

    @fields.depends('is_active', 'course_completed')
    def on_change_with_discontinued(self):
        return not (self.is_active or self.course_completed)

    @fields.depends('is_active', 'discontinued')
    def on_change_with_course_completed(self):
        return not (self.is_active or self.discontinued)

    @staticmethod
    def default_is_active():
        return True
Beispiel #13
0
class PurchaseLine(ModelSQL, ModelView):
    """
    Fleet Management Purchase Line
    """
    _name = 'purchase.line'

    product_fleet_type = fields.Function(
        fields.Char('Product_Fleet_Type', on_change_with=['product']),
        'get_product_fleet_type')

    asset = fields.Many2One("fleet.asset",
                            "Asset",
                            states={
                                'invisible':
                                Not(Equal(Eval('product_fleet_type'), 'fuel')),
                                'required':
                                Equal(Eval('product_fleet_type'), 'fuel')
                            },
                            depends=['product_fleet_type'])

    meter_reading = fields.BigInteger(
        "Meter Reading",
        states={
            'invisible': Not(Equal(Eval('product_fleet_type'), 'fuel')),
            'required': (Eval('product_fleet_type') == 'fuel')
        },
        depends=['product_fleet_type'])

    fuel_efficiency = fields.Function(
        fields.Float("Fuel Efficiency",
                     states={
                         'invisible':
                         Not(Equal(Eval('product_fleet_type'), 'fuel')),
                     },
                     depends=['product_fleet_type']), 'get_fuel_efficiency')

    def get_product_fleet_type(self, ids, name):
        """Get the product type.
        """
        res = {}
        for purchase_line in self.browse(ids):
            res[purchase_line.id] = purchase_line.product.fleet_management_type
        return res

    def get_fuel_efficiency(self, ids, name):
        """Return the fuel efficiency
        """
        res = {}
        for purchase_line in self.browse(ids):
            efficiency = 0.00
            previous_line_ids = self.search(
                [('asset', '=', purchase_line.asset.id),
                 ('purchase.purchase_date', '<',
                  purchase_line.purchase.purchase_date)],
                limit=1)
            if previous_line_ids:
                previous_line = self.browse(previous_line_ids[0])
                efficiency = (purchase_line.meter_reading - \
                    previous_line.meter_reading) / purchase_line.quantity
            res[purchase_line.id] = efficiency
        return res

    def on_change_with_product_fleet_type(self, vals):
        """Set the value of product_fleet_type so that the invisible and 
        required property may be set
        """
        product_obj = Pool().get('product.product')
        if vals.get('product'):
            product = product_obj.browse(vals['product'])
            return product.fleet_management_type
        return None
Beispiel #14
0
class Exam(Workflow, ModelSQL, ModelView):
    '''Exam'''

    __name__ = 'exam_section.exam'

    state = fields.Selection([
        ('draft', 'Draft'),
        ('confirm', 'Confirm'),
        ('approved', 'Approved'),
        ('in_progress', 'In Progress'),
        ('awaiting_ta_da', 'Awaiiting TA/DA Bills'),
        ('ta_da_submit', 'TA/DA Submit'),
        ('approval', 'Approval'),
        ('budget_allocation', 'Budget Allocation'),
        ('final_process', 'Final Process')
    ], 'Status', readonly=True)
    name = fields.Char('Exam Name')
    exam_type = fields.Many2One(
        'exam_section.exam_type',
        'Exam Type',
        states={
            'readonly': ~Eval('state').in_(['draft'])
        },
        depends=['state'],
        required=True)
    date_from = fields.Date('Date From', states={
            'readonly': ~Eval('state').in_(['draft'])
            }, depends=['state'], required=True)
    date_to = fields.Date('Date To', states={
            'readonly': ~Eval('state').in_(['draft'])
            }, depends=['state'], required=True)
    centers = fields.One2Many('exam.centers', 'exam', 'Centers', states={
            'readonly': ~Eval('state').in_(['draft'])
            }, depends=['state'])
    employees = fields.One2Many('exam.employees', 'exam', 'Employees', states={
            'readonly': ~Eval('state').in_(['draft'])
            }, depends=['state'])
    renumeration_bills = fields.One2Many(
        'exam_section.renumeration_bill',
        'exam',
        'Renumeration Bills',
        states={
            'readonly': ~Eval('state').in_(['draft']),
            'invisible': ~Eval('state').in_([
                'draft',
                'confirm',
                'approved',
                'in_progress'
            ]),
        },
        depends=['state']
    )
    ta_da_bills = fields.One2Many(
        'exam_section.ta_da_bill',
        'exam',
        'TA/DA Bills',
        states={
            'readonly': ~Eval('state').in_(['draft']),
            'invisible': ~Eval('state').in_([
                'draft',
                'confirm',
                'approved',
                'in_progress'
            ]),
        },
        depends=['state']
    )
    total_renumeration_cost = fields.Function(
        fields.Float('Total Renumeration Cost'),
        'get_total_renumeration')
    total_ta_da_cost = fields.Function(
        fields.Float('Total TA/DA Cost'),
        'get_total_ta_da')

    def get_total_renumeration(self, name):
        res = 0
        if self.employees:
            for employee in self.employees:
                if employee.renumeration_bill:
                    res += employee.renumeration_bill.net_amount
        return res

    def get_total_ta_da(self, name):
        res = 0
        if self.employees:
            for employee in self.employees:
                if employee.ta_da_bill:
                    res += employee.ta_da_bill.total_amount
        return res

    @fields.depends('exam_type', 'date_from')
    def on_change_with_name(self):
            return '{} {}'.format(
                self.exam_type.name if self.exam_type else '',
                self.date_from.year if self.date_from else '',
            )
    
    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._transitions = set((
            ('draft', 'confirm'),
            ('confirm', 'approved'),
            ('approved', 'in_progress'),
            ('in_progress', 'awaiting_ta_da'),
            ('awaiting_ta_da', 'ta_da_submit'),
            ('ta_da_submit', 'approval'),
            ('approval', 'budget_allocation'),
            ('budget_allocation', 'final_process')
        ))
        cls._buttons.update({
            'confirm_data': {
                'invisible': ~Eval('state').in_(['draft'])
            },
            'approve': {
                'invisible': ~Eval('state').in_(['confirm'])
            },
            'send_in_progress': {
                'invisible': ~Eval('state').in_(['approved'])
            },
            'generate_ta_da_bills': {
                'invisible': ~Eval('state').in_(['in_progress'])
            },
            'submit_ta_da_bills': {
                'invisible': ~Eval('state').in_(['awaiting_ta_da'])
            },
            'approve_ta_da': {
                'invisible': ~Eval('state').in_(['ta_da_submit'])
            },
            'send_for_budget_allocation': {
                'invisible': ~Eval('state').in_(['approval'])
            },
            'send_for_final_process': {
                'invisible': ~Eval('state').in_(['budget_allocation'])
            }
        })

    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def get_current_exam():
        exam_record = list(Transaction().timestamp.keys())[0]
        exam_table_and_id = exam_record.split(',')
        exam_table = exam_table_and_id[0]
        exam_id = exam_table_and_id[1]
        pool = Pool()
        Exam = pool.get(exam_table)
        current_exam = Exam.search([('id', '=', exam_id)])[0]
        return current_exam

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

    @classmethod
    @Workflow.transition('approved')
    def approve(cls, records):
        current_exam = cls.get_current_exam()
        Renum = Pool().get('exam_section.renumeration_bill')
        for current_employee in current_exam.employees:
            renum_bill_for_employee = Renum.create([{
                'type_of_examiner': 'internal',
                'exam': current_exam,
                'employee': current_employee.employee,
                'designation': current_employee.employee.designation.name,
            }])[0]
            current_employee.renumeration_bill = renum_bill_for_employee
            current_employee.renumeration_bill.save()
            current_employee.save()


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

    @classmethod
    @Workflow.transition('awaiting_ta_da')
    def generate_ta_da_bills(cls, records):
        TADA = Pool().get('exam_section.ta_da_bill')
        current_exam = cls.get_current_exam()
        current_date_from = current_exam.date_from
        current_date_to = current_exam.date_to
        for current_employee in current_exam.employees:
            ta_da_bill_for_employee = TADA.create([{
                'employee': current_employee.employee,
                'designation': 
                    current_employee.employee.designation,
                'department':
                    current_employee.employee.department,
                'exam': current_exam,
                'purpose':
                    'On deputation for {} examination from {} to {}'.format(
                        current_exam.name,
                        current_date_from.strftime("%d %B %Y"),
                        current_date_to.strftime("%d %B %Y")
                    )
            }])[0]
            current_employee.ta_da_bill = ta_da_bill_for_employee
            current_employee.ta_da_bill.save()
            current_employee.save()

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

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

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

    @classmethod
    @Workflow.transition('final_process')
    def send_for_final_process(cls, records):
        pass
Beispiel #15
0
class HouseBuildingLoan(Workflow, ModelSQL, ModelView):
    'House Building Loan'
    __name__ = 'hba.loan'

    salary_code = fields.Char('Salary Code',
                              states={
                                  'readonly': ~Eval('state').in_(['draft']),
                              },
                              depends=['state'],
                              required=True)
    employee = fields.Many2One('company.employee',
                               'Name of Applicant',
                               states={
                                   'readonly': ~Eval('state').in_(['draft']),
                               },
                               depends=['state'],
                               required=True)
    designation = fields.Many2One("employee.designation",
                                  "Designation",
                                  states={
                                      'readonly':
                                      ~Eval('state').in_(['draft']),
                                  },
                                  depends=['state'],
                                  required=True)
    department = fields.Many2One("company.department",
                                 "Department",
                                 states={
                                     'readonly': ~Eval('state').in_(['draft']),
                                 },
                                 depends=['state'],
                                 required=True)
    post_held = fields.Selection([
        ('permanent', 'Permanent'), ('temporary_offg', 'Temporary/Offg'),
        ('length_of_service', 'Length of service on the date of application')
    ],
                                 string='Post held',
                                 sort=False,
                                 states={
                                     'readonly': ~Eval('state').in_(['draft']),
                                 },
                                 depends=['state'],
                                 required=True)
    place_of_posting = fields.Char('Place of Posting',
                                   states={
                                       'readonly':
                                       ~Eval('state').in_(['draft']),
                                   },
                                   depends=['state'],
                                   required=True)
    scale_pay = fields.Char('Scale Pay',
                            states={
                                'readonly': ~Eval('state').in_(['draft']),
                            },
                            depends=['state'],
                            required=True)
    pension_rule = fields.Selection([
        ('nps', 'NPS'),
        ('gpf', 'GPF'),
    ],
                                    string='Pension Rule',
                                    sort=False,
                                    states={
                                        'readonly':
                                        ~Eval('state').in_(['draft']),
                                    },
                                    depends=['state'],
                                    required=True)
    retirement_date = fields.Date('Date of Retirement', required=True)
    advance = fields.Selection(
        [('yes', 'Yes'), ('no', 'No')],
        string=
        'Any Other advance/Final withdrawal taken for purchase of land/construction',
        sort=False,
        required=True)
    advance_needed = fields.Selection(
        [('plot', 'Purchase of Plot'),
         ('construction', 'Construction of new House'),
         ('enlarging_house', 'Enlarging existing House'),
         ('ready_built', 'Purchase of Ready Built House'),
         ('own_house', 'Own House')],
        string='Advance Needed for',
        sort=False,
        required=True)
    advance_amount = fields.Float('Amount',
                                  states={
                                      'invisible':
                                      ~Eval('advance').in_(['yes']),
                                  },
                                  depends=['advance'])
    source = fields.Char('Source',
                         states={
                             'invisible': ~Eval('advance').in_(['yes']),
                         },
                         depends=['advance'])
    attest_copy = fields.Many2One('ir.attachment',
                                  'attested Copy',
                                  states={
                                      'invisible':
                                      ~Eval('advance').in_(['yes']),
                                  },
                                  depends=['advance'])
    ######################################################
    location_address = fields.Char(
        'Location with Address',
        states={
            'invisible':
            ~Eval('advance_needed').in_(['plot', 'enlarging_house']),
        },
        depends=['advance_needed'])
    location_type = fields.Selection([
        ('rural', 'Rural'),
        ('urbun', 'Urban'),
    ],
                                     string='Location Type',
                                     sort=False,
                                     states={
                                         'invisible':
                                         ~Eval('advance_needed').in_(['plot']),
                                     },
                                     depends=['advance_needed'])
    clearly = fields.Selection([('yes', 'Yes'), ('no', 'No')],
                               string='Is it Clearly Demacrated & Developed',
                               sort=False,
                               states={
                                   'invisible':
                                   ~Eval('advance_needed').in_(['plot']),
                               },
                               depends=['advance_needed'])
    area = fields.Float('Apporximate area(in sq.mts.)',
                        states={
                            'invisible': ~Eval('advance_needed').in_(['plot']),
                        },
                        depends=['advance_needed'])
    cost = fields.Float('cost',
                        states={
                            'invisible': ~Eval('advance_needed').in_(['plot']),
                        },
                        depends=['advance_needed'])
    amount = fields.Float('Amount actually paid',
                          states={
                              'invisible':
                              ~Eval('advance_needed').in_(['plot']),
                          },
                          depends=['advance_needed'])
    acquired = fields.Char('If not Purchase when proposed to be acquired',
                           states={
                               'invisible':
                               ~Eval('advance_needed').in_(['plot']),
                           },
                           depends=['advance_needed'])
    unexpired = fields.Char('Unexpired portion of lease if not free hold',
                            states={
                                'invisible':
                                ~Eval('advance_needed').in_(['plot']),
                            },
                            depends=['advance_needed'])
    amount_required = fields.Float('Amount of advance required', required=True)
    num_installment = fields.Integer('No. of instalments for repayment',
                                     required=True)
    ########################################################
    construction = fields.One2Many(
        'construction',
        'hba_loan',
        'Construction',
        states={
            'invisible': ~Eval('advance_needed').in_(['construction']),
        },
        depends=['advance_needed'])
    floor = fields.Integer('Floor-wise area to be constructed')
    plinth_area = fields.Float('Plinth area(in sq.mtrs.')
    enlargement = fields.Float(
        'Plinth area proposed for enlargement(in sq. mtrs.)')
    cunstr_cost = fields.Float('Construction Cost')
    enlargement_cost = fields.Float('Cost of proposed enlargement')
    total_plinth = fields.Float('Total Plinth area')
    total_cost = fields.Float('Total Cost')
    constructed = fields.Date('When Constructed')
    price_settled = fields.Float('Price Settled')
    agency = fields.Char('The Agency from whom to be purchased')
    already_paid = fields.Float('Already paid')
    to_be_paid = fields.Float('To be Paid')
    own_house = fields.One2Many('own.house',
                                'hba_loan',
                                'Own House',
                                states={
                                    'invisible':
                                    ~Eval('advance_needed').in_(['own_house']),
                                },
                                depends=['advance_needed'])
    loan_line = fields.One2Many('hba.loan.line',
                                'loan',
                                'Installment Lines',
                                readonly=True)
    state = fields.Selection([('draft', 'Draft'),
                              ('forwarded_to_jo', 'Forwarded to JO'),
                              ('forwarded_to_ao', 'Forwarded to AO'),
                              ('approve', 'Approved'), ('cancel', 'Cancel')],
                             'Status',
                             readonly=True,
                             sort=False)
    cancel_reason = fields.Char(
        'Cancel Reason',
        states={
            'invisible': ~Eval('state').in_(['forwarded_to_ao', 'cancel']),
            'readonly': ~Eval('state').in_(['forwarded_to_ao']),
        },
        depends=['state'],
    )

    @staticmethod
    def default_state():
        return 'draft'

    @classmethod
    def view_attributes(cls):
        plot = ~Eval('advance_needed').in_(['plot'])
        construction = ~Eval('advance_needed').in_(['construction'])
        enlarging_house = ~Eval('advance_needed').in_(['enlarging_house'])
        ready_built = ~Eval('advance_needed').in_(['ready_built'])
        own_house = ~Eval('advance_needed').in_(['own_house'])

        attribute = [
            ("//page[@id='plot']", "states", {
                "invisible": plot
            }),
            ("//page[@id='construction']", "states", {
                "invisible": construction
            }),
            ("//page[@id='existing_house']", "states", {
                "invisible": enlarging_house
            }),
            ("//page[@id='build_house']", "states", {
                "invisible": ready_built
            }),
            ("//page[@id='own_house']", "states", {
                "invisible": own_house
            }),
        ]
        return attribute

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

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._transitions |= set((
            ('draft', 'forwarded_to_jo'),
            ('forwarded_to_jo', 'forwarded_to_ao'),
            ('forwarded_to_ao', 'approve'),
            ('forwarded_to_ao', 'cancel'),
        ))
        cls._buttons.update({
            'submitted_to_ao': {
                'invisible': ~Eval('state').in_(['draft']),
                'depends': ['state'],
            },
            'forward_to_jo': {
                'invisible': ~Eval('state').in_(['forwarded_to_jo']),
                'depends': ['state'],
            },
            'forward_to_ao': {
                'invisible': ~Eval('state').in_(['forwarded_to_ao']),
                'depends': ['state'],
            },
            'cancel': {
                'invisible': ~Eval('state').in_(['forwarded_to_ao']),
                'depends': ['state'],
            },
        })

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

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

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

    @classmethod
    @ModelView.button
    @Workflow.transition('cancel')
    def cancel(cls, records):
        for record in records:
            if not record.cancel_reason:
                cls.raise_user_error('Please fill the Cancel reason')
        pass

    @classmethod
    def loan_installment(cls, records):
        count = 0
        LoanLine = Pool().get('hba.loan.line')
        for loan in records:
            amount = (loan.amount_required / loan.num_installment)
            for line in range(1, int(loan.num_installment) + 1):
                mydate = datetime.datetime.now().month
                month = mydate - 1
                if month + line > 12:
                    count += 1
                    if count > 12:
                        count = 1
                    months = datetime.date(1900, count, 1).strftime('%B')
                else:
                    months = datetime.date(1900, month + line,
                                           1).strftime('%B')
                vals = {
                    'month': months,
                    'amount': amount,
                    'status': 'pending',
                    'loan': loan.id
                }
                line = LoanLine.create([vals])
Beispiel #16
0
class BudgetLineChange(ModelSQL, ModelView):
    'General Budget Lines Changes'
    _name = 'ekd.account.budget.line.change'
    _description = __doc__

    budget_line = fields.Many2One('ekd.account.budget.line',
                                  'Budget Line',
                                  ondelete="RESTRICT")
    budget = fields.Many2One('ekd.account.budget',
                             'Budget',
                             ondelete="RESTRICT")
    date_change = fields.Date('Date Change', required=True)
    direct_line = fields.Function(fields.Char('Direct Budget'), 'get_fields')
    income = fields.Function(fields.Boolean('Income Budget'), 'get_fields')
    kind = fields.Function(fields.Char('Kind'), 'get_fields')
    type_line = fields.Function(fields.Char('Type Line'), 'get_fields')
    analytic = fields.Function(
        fields.Many2One('ekd.account.analytic', 'Analytic Account'),
        'get_fields')
    name = fields.Function(fields.Char('Name'), 'get_fields')
    note = fields.Text('Note')
    product = fields.Many2One('product.product', 'Product')
    quantity = fields.Float('Quantity',
                            digits=(16, Eval('unit_digits', 2)),
                            on_change=['quantity', 'price_unit'],
                            states={
                                'invisible':
                                Or(Equal(Eval('kind'), 'amount'),
                                   Not(Equal(Eval('type_line'), 'line'))),
                                'tree_invisible':
                                Or(Equal(Eval('kind'), 'amount'),
                                   Equal(Eval('type_line'), 'line'))
                            },
                            depends=['kind', 'type_line'])
    uom = fields.Many2One('product.uom',
                          'Unit',
                          states={
                              'invisible': Equal(Eval('kind'), 'amount'),
                              'tree_invisible': Equal(Eval('kind'), 'amount')
                          },
                          depends=['kind', 'type_line'])
    unit_digits = fields.Function(
        fields.Integer('Unit Digits', on_change_with=['uom']),
        'get_unit_digits')
    price_unit = fields.Numeric('Price unit',
                                digits=(16, 2),
                                states={
                                    'invisible':
                                    Or(Equal(Eval('kind'), 'amount'),
                                       Not(Equal(Eval('type_line'), 'line'))),
                                    'tree_invisible':
                                    Or(Equal(Eval('kind'), 'amount'),
                                       Not(Equal(Eval('type_line'), 'line')))
                                },
                                depends=['kind', 'type_line'],
                                on_change=['quantity', 'price_unit'])
    amount = fields.Numeric(
        'Amount',
        digits=(16, 2),
        states={'invisible': Equal(Eval('kind'), 'quantity')})
    state = fields.Selection([('draft', 'Draft'), ('confirmed', 'Confirmed'),
                              ('deleted', 'Deleted')],
                             'State',
                             required=True,
                             readonly=True)
    employee_confirm = fields.Many2One('res.user', 'Employee Confirmed')

    def __init__(self):
        super(BudgetLineChange, self).__init__()
        #        self._sql_constraints += [
        #            ('code_uniq', 'UNIQUE(budget,sequence)', 'The sequence in the budget must be unique!'),
        #        ]
        self._order.insert(0, ('budget_line', 'ASC'))

    def default_state(self):
        return 'draft'

    def default_budget(self):
        return Transaction().context.get('budget') or False

    def default_name(self):
        return Transaction().context.get('name') or False

    def default_uom(self):
        return Transaction().context.get('uom') or False

    def default_quantity(self):
        return Transaction().context.get('quantity') or False

    def default_price_unit(self):
        return Transaction().context.get('price_unit') or False

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

    def get_unit_digits(self, ids, name):
        res = {}
        for line in self.browse(ids):
            if line.uom:
                res[line.id] = line.uom.digits
            else:
                res[line.id] = 2
        return res

    def get_fields(self, ids, names):
        res = {}
        for line in self.browse(ids):
            if line.budget_line:
                for name in names:
                    res.setdefault(name, {})
                    res[name].setdefault(line.id, False)
                    if name == 'analytic':
                        res[name][line.id] = line.budget_line.analytic.id
                    elif name == 'name':
                        res[name][line.id] = line.budget_line.name
                    elif name == 'kind':
                        res[name][line.id] = line.budget_line.kind
                    elif name == 'direct_line':
                        res[name][line.id] = line.budget_line.direct_line
                    elif name == 'type_line':
                        res[name][line.id] = line.budget_line.type_line
        return res

    def on_change_with_name(self, vals):
        if not vals.get('analytic'):
            return
        analytic_obj = self.pool.get('ekd.account.analytic')
        if not vals.get('name'):
            return analytic_obj.browse(vals.get('analytic')).rec_name

    def on_change_with_unit_digits(self, vals):
        if not vals.get('uom'):
            return 2
        uom_obj = self.pool.get('product.uom')
        return uom_obj.browse(vals.get('uom')).digits

    def on_change_with_amount(self, vals):
        if not vals.get('quantity') or not vals.get('price_unit'):
            return vals
        return Decimal(str(vals.get('quantity'))) * Decimal(
            str(vals.get('price_unit')))

    def on_change_quantity(self, vals):
        if not vals.get('quantity') or not vals.get('price_unit'):
            return vals
        return {
            'amount':
            Decimal(str(vals.get('quantity'))) *
            Decimal(str(vals.get('price_unit')))
        }

    def on_change_price_unit(self, vals):
        if not vals.get('quantity') or not vals.get('price_unit'):
            return vals
        return {
            'amount':
            Decimal(str(vals.get('quantity'))) *
            Decimal(str(vals.get('price_unit')))
        }

    def delete(self, ids):
        return super(BudgetLineChange, self).delete(ids)

    def create(self, vals):
        return super(BudgetLineChange, self).create(vals)

    def write(self, ids, vals):
        return super(BudgetLineChange, self).write(ids, vals)
Beispiel #17
0
class GPFSubscription(Workflow, ModelSQL, ModelView):
    'GPF Subscription'
    __name__ = 'gpf.subscription'

    salary_code = fields.Char('Salary Code',
                              states={
                                  'readonly': ~Eval('state').in_(['draft']),
                              },
                              depends=['state'])
    employee = fields.Many2One('company.employee',
                               'Employee',
                               states={
                                   'readonly': ~Eval('state').in_(['draft']),
                               },
                               depends=['state'])
    doj = fields.Date('Date of Joining',
                      states={
                          'readonly': ~Eval('state').in_(['draft']),
                      },
                      depends=['state'])
    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'])
    gpf_number = fields.Char('G.P.F.Number',
                             states={
                                 'readonly': ~Eval('state').in_(['draft']),
                             },
                             depends=['state'])
    basic_pay = fields.Float('Basic Pay',
                             states={
                                 'readonly': ~Eval('state').in_(['draft']),
                             },
                             depends=['state'])
    change_sub = fields.Selection(
        [
            ('increase', 'Increase'),
            ('decrease', 'Decrease'),
        ],
        string='Change Subscription',
        required=True,
        sort=False,
        states={
            'readonly': ~Eval('state').in_(['draft', 'waiting_for_otp']),
        },
        depends=['state'])
    pre_amount = fields.Float('Previous Amount',
                              states={
                                  'readonly': ~Eval('state').in_(['draft']),
                              },
                              depends=['state'])
    curr_amount = fields.Float(
        'Current Amount',
        required=True,
        states={
            'readonly': ~Eval('state').in_(['draft', 'waiting_for_otp']),
        },
        depends=['state'])
    otp = fields.Char(
        'OTP',
        size=6,
        states={
            'invisible': ~Eval('state').in_(['waiting_for_otp']),
        },
        depends=['state'],
    )
    otp_generate = fields.Char('OTP Generated')
    otp_generate_time = fields.DateTime('OTP Generated Time')
    state = fields.Selection([
        ('draft', 'Draft'),
        ('waiting_for_otp', 'Waiting For OTP'),
        ('submitted_to_ao', 'Forwarded to AO'),
        ('approved', 'Approved'),
        ('cancel_gpf', 'Cancel'),
    ],
                             'Status',
                             readonly=True,
                             sort=False)
    contract = fields.Many2One('hr.contract',
                               'Salary Details',
                               states={'invisible': ~Eval('state').in_([])})
    reason = fields.Char(
        'Reason',
        states={
            'invisible': ~Eval('state').in_(['submitted_to_ao', 'cancel_gpf']),
            'readonly': ~Eval('state').in_(['submitted_to_ao']),
        },
        depends=['state'],
    )
    approve_date = fields.Date(
        'Date of Approve',
        states={
            'invisible': ~Eval('state').in_(['approved']),
            'readonly': Eval('state').in_(['approved']),
        },
        depends=['state'],
    )
    gpf_balance = fields.Float('G.P.F.Amount')

    @staticmethod
    def default_employee():
        pool = Pool()
        User = pool.get('res.user')
        user = User(Transaction().user)
        employee = user.employee
        return employee.id if employee else None

    @fields.depends('employee')
    def on_change_employee(self, name=None):
        if self.employee:
            self.salary_code = self.employee.salary_code
            self.doj = self.employee.date_of_joining
            self.designation = self.employee.designation
            self.department = self.employee.department
            self.gpf_number = self.employee.gpf_number
            pool = Pool()
            hrcontract = pool.get('hr.contract')
            contracts = hrcontract.search([('employee', '=', self.employee),
                                           ('active', '<=', True)])
            for contract in contracts:
                self.contract = contract.id
                self.basic_pay = contract.basic
                self.pre_amount = contract.gpf_amount

    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def default_change_sub():
        return 'increase'

    @classmethod
    def validate(cls, records):
        super(GPFSubscription, cls).validate(records)
        for record in records:
            approvedate = datetime.now() - relativedelta(months=3)
            # gpf = cls.search([
            #         ('id', '!=', record.id),
            #         ('employee', '=', record.employee),
            #         ('approve_date', '>=', approvedate),
            #         ('state', '=', 'approved')
            #         ])
            gpf_increase = cls.search([('id', '!=', record.id),
                                       ('employee', '=', record.employee),
                                       ('change_sub', '=', 'increase'),
                                       ('state', '=', 'approved')])
            gpf_decrease = cls.search([('id', '!=', record.id),
                                       ('employee', '=', record.employee),
                                       ('change_sub', '=', 'decrease'),
                                       ('state', '=', 'approved')])
            # if gpf:
            #     cls.raise_user_error('Need for 3 Month GAP')
            if record.change_sub == 'increase' and len(gpf_increase) >= 2:
                cls.raise_user_error('Already 2 times increase')
            elif record.change_sub == 'decrease' and len(gpf_decrease) >= 1:
                cls.raise_user_error('Already 1 times decrease')
            record.valid_curr_amount()

    def valid_curr_amount(self):
        if self.basic_pay:
            basic = (6 * self.basic_pay) / 100
        if self.basic_pay < self.curr_amount:
            self.raise_user_error('Current Amount maximum of basic amount')
        elif basic > self.curr_amount:
            self.raise_user_error('Current Amount minmum of basic amount 6%')
        elif (self.change_sub == 'increase'
              and self.pre_amount >= self.curr_amount):
            self.raise_user_error('Current Amount minmum of Previous amount')
        elif (self.change_sub == 'decrease'
              and self.pre_amount <= self.curr_amount):
            self.raise_user_error('Current Amount maximum of Previous amount')

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._error_messages.update({
            'otp_error': "OTP is not fill",
        })
        cls._transitions |= set((
            ('draft', 'waiting_for_otp'),
            ('waiting_for_otp', 'submitted_to_ao'),
            ('submitted_to_ao', 'approved'),
            ('submitted_to_ao', 'cancel_gpf'),
        ))
        cls._buttons.update({
            'waiting_for_otp': {
                'invisible': ~Eval('state').in_(['draft']),
                'depends': ['state'],
            },
            'submitted_to_ao': {
                'invisible': ~Eval('state').in_(['waiting_for_otp']),
                'depends': ['state'],
            },
            'approved': {
                'invisible': ~Eval('state').in_(['submitted_to_ao']),
                'depends': ['state'],
            },
            'cancel_gpf': {
                'invisible': ~Eval('state').in_(['submitted_to_ao']),
                'depends': ['state'],
            },
        })

    @classmethod
    def create(cls, values):
        '''Overwrite the method to manage the parts.'''
        for vals in values:
            gpf_increase = cls.search([('employee', '=', vals['employee']),
                                       ('change_sub', '=', 'increase'),
                                       ('state', 'not in', ('approved',
                                                            'cancel_gpf'))])
            gpf_decrease = cls.search([('employee', '=', vals['employee']),
                                       ('change_sub', '=', 'decrease'),
                                       ('state', 'not in', ('approved',
                                                            'cancel_gpf'))])
            if gpf_increase:
                cls.raise_user_error('Please Approve Previous GPF')
            if gpf_decrease:
                cls.raise_user_error('Please Approve Previous GPF')
        return super().create(values)

    def notify_otp_to_user(self):
        message = """Dear {name}, OTP to digitally submitted_to_ao
            the GPF form is {otp}""".format(name=self.employee.party.name,
                                            otp=self.otp_generate)
        # TODO: Get the value of logged in user's employee.
        contact = self.employee.party.contact_mechanism_get('mobile')
        if not contact:
            self.raise_user_error("missing_contact")
        number = contact.value
        number = 9811292339
        params = {
            'msg': message + " (%s)" % contact.value,
            'mobile': number,
        }
        url = "http://192.168.185.17/trytonsms/sendsms.aspx"
        requests.get(url, params)
        return True

    def generate_otp(self):
        # Generate OTP for the user
        self.otp_generate = ''.join(
            ["%s" % random.randint(0, 9) for num in range(0, 6)])
        self.otp_generate_time = datetime.now()
        self.save()
        self.notify_otp_to_user()

    def validate_otp(self):
        # Validate OTP
        if self.otp != self.otp_generate:
            self.raise_user_error('invalid_otp')
        return True

    @classmethod
    @ModelView.button
    @Workflow.transition('waiting_for_otp')
    def waiting_for_otp(cls, records):
        for record in records:
            record.generate_otp()
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('submitted_to_ao')
    def submitted_to_ao(cls, records):
        for record in records:
            record.validate_otp()
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('approved')
    def approved(cls, records):
        cls.set_gpf_amount(records)

    @classmethod
    @ModelView.button
    @Workflow.transition('cancel_gpf')
    def cancel_gpf(cls, records):
        for record in records:
            if not record.reason:
                cls.raise_user_error('Please fill the reason')
        pass

    @classmethod
    def set_gpf_amount(cls, records):
        '''
        Fill the number field with the sale sequence
        '''
        pool = Pool()
        contract = pool.get('hr.contract')
        for gpf in records:
            if not gpf.otp:
                cls.raise_user_error('OTP is not fill')
            if gpf.contract:
                gpf.approve_date = datetime.now().date()
                gpf.save()
                contract.write([gpf.contract], {
                    'gpf_amount': gpf.curr_amount,
                })
Beispiel #18
0
class SaleLine(ModelSQL, ModelView):
    'Sale Line'
    __name__ = 'sale.line'
    _rec_name = 'description'
    sale = fields.Many2One('sale.sale',
                           'Sale',
                           ondelete='CASCADE',
                           select=True)
    sequence = fields.Integer('Sequence')
    type = fields.Selection([
        ('line', 'Line'),
    ],
                            'Type',
                            select=True,
                            required=True)
    quantity = fields.Float('Quantity',
                            digits=(16, Eval('unit_digits', 2)),
                            states={
                                'invisible': Eval('type') != 'line',
                                'required': Eval('type') == 'line',
                                'readonly': ~Eval('_parent_sale', {}),
                            },
                            depends=['type', 'unit_digits'])
    unit = fields.Many2One(
        'product.uom',
        'Unit',
        states={
            'required': Bool(Eval('product')),
            'invisible': Eval('type') != 'line',
            'readonly': ~Eval('_parent_sale', {}),
        },
        domain=[
            If(Bool(Eval('product_uom_category')),
               ('category', '=', Eval('product_uom_category')),
               ('category', '!=', -1)),
        ],
        depends=['product', 'type', 'product_uom_category'])
    unit_digits = fields.Function(fields.Integer('Unit Digits'),
                                  'on_change_with_unit_digits')
    product = fields.Many2One('product.product',
                              'Product',
                              states={
                                  'invisible': Eval('type') != 'line',
                                  'readonly': ~Eval('_parent_sale', {}),
                              },
                              depends=['type'])
    product_uom_category = fields.Function(
        fields.Many2One('product.uom.category', 'Product Uom Category'),
        'on_change_with_product_uom_category')
    unit_price = fields.Numeric('Unit Price',
                                digits=(16, 4),
                                states={
                                    'invisible': Eval('type') != 'line',
                                    'required': Eval('type') == 'line',
                                },
                                depends=['type'])
    amount = fields.Function(
        fields.Numeric('Amount',
                       digits=(16, Eval('_parent_sale',
                                        {}).get('currency_digits', 2)),
                       states={
                           'invisible':
                           ~Eval('type').in_(['line', 'subtotal']),
                           'readonly': ~Eval('_parent_sale'),
                       },
                       depends=['type']), 'get_amount')
    description = fields.Text('Description', size=None, required=True)

    unit_price_w_tax = fields.Function(
        fields.Numeric('Unit Price with Tax',
                       digits=(16, Eval('_parent_sale',
                                        {}).get('currency_digits',
                                                Eval('currency_digits', 2))),
                       states={
                           'invisible': Eval('type') != 'line',
                       },
                       depends=['type', 'currency_digits']),
        'get_price_with_tax')
    amount_w_tax = fields.Function(
        fields.Numeric('Amount with Tax',
                       digits=(16, Eval('_parent_sale',
                                        {}).get('currency_digits',
                                                Eval('currency_digits', 2))),
                       states={
                           'invisible':
                           ~Eval('type').in_(['line', 'subtotal']),
                       },
                       depends=['type', 'currency_digits']),
        'get_price_with_tax')
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
                                      'on_change_with_currency_digits')
    currency = fields.Many2One('currency.currency',
                               'Currency',
                               states={
                                   'required': ~Eval('sale'),
                               },
                               depends=['sale'])

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

        # Allow edit product, quantity and unit in lines without parent sale
        for fname in ('product', 'quantity', 'unit'):
            field = getattr(cls, fname)
            if field.states.get('readonly'):
                del field.states['readonly']

    @staticmethod
    def default_type():
        return 'line'

    @staticmethod
    def default_sale():
        if Transaction().context.get('sale'):
            return Transaction().context.get('sale')
        return None

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

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

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

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

    def _get_context_sale_price(self):
        context = {}
        if getattr(self, 'sale', None):
            if getattr(self.sale, 'currency', None):
                context['currency'] = self.sale.currency.id
            if getattr(self.sale, 'party', None):
                context['customer'] = self.sale.party.id
            if getattr(self.sale, 'sale_date', None):
                context['sale_date'] = self.sale.sale_date
        if self.unit:
            context['uom'] = self.unit.id
        else:
            context['uom'] = self.product.default_uom.id
        return context

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

    def get_amount(self, name):
        if self.type == 'line':
            return self.on_change_with_amount()
        elif self.type == 'subtotal':
            amount = Decimal('0.0')
            for line2 in self.sale.lines:
                if line2.type == 'line':
                    amount += line2.sale.currency.round(
                        Decimal(str(line2.quantity)) * line2.unit_price)
                elif line2.type == 'subtotal':
                    if self == line2:
                        break
                    amount = Decimal('0.0')
            return amount
        return Decimal('0.0')

    @fields.depends('product', 'unit', 'quantity', 'description',
                    '_parent_sale.party', '_parent_sale.currency',
                    '_parent_sale.sale_date')
    def on_change_product(self):
        Product = Pool().get('product.product')
        if not self.product:
            return {}
        res = {}

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

        category = self.product.default_uom.category
        if not self.unit or self.unit not in category.uoms:
            res['unit'] = self.product.default_uom.id
            self.unit = self.product.default_uom
            res['unit.rec_name'] = self.product.default_uom.rec_name
            res['unit_digits'] = self.product.default_uom.digits

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

        self.unit_price = res['unit_price']
        self.type = 'line'
        res['amount'] = self.on_change_with_amount()
        res['description'] = Product(self.product.id).name
        return res

    @fields.depends('product', 'quantity', 'unit', '_parent_sale.currency',
                    '_parent_sale.party', '_parent_sale.sale_date')
    def on_change_quantity(self):
        Product = Pool().get('product.product')

        if not self.product:
            return {}
        res = {}

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

    @fields.depends('type', 'quantity', 'unit_price', 'unit',
                    '_parent_sale.currency')
    def on_change_with_amount(self):
        if self.type == 'line':
            currency = self.sale.currency if self.sale else None
            amount = Decimal(str(self.quantity or '0.0')) * \
                (self.unit_price or Decimal('0.0'))
            if currency:
                return currency.round(amount)
            return amount
        return Decimal('0.0')

    @classmethod
    def get_price_with_tax(cls, lines, names):
        pool = Pool()
        amount_w_tax = {}
        unit_price_w_tax = {}

        def compute_amount_with_tax(line):

            if line.product.taxes_category == True:
                impuesto = line.product.category.taxes
            else:
                impuesto = line.product.taxes
            if impuesto == 'iva0':
                value = Decimal(0.0)
            elif impuesto == 'no_iva':
                value = Decimal(0.0)
            elif impuesto == 'iva12':
                value = Decimal(0.12)
            elif impuesto == 'iva14':
                value = Decimal(0.14)
            else:
                value = Decimal(0.0)

            tax_amount = line.unit_price * value
            return line.get_amount(None) + tax_amount

        for line in lines:
            amount = Decimal('0.0')
            unit_price = Decimal('0.0')
            currency = (line.sale.currency if line.sale else line.currency)

            if line.type == 'line':
                if line.quantity and line.product:
                    amount = compute_amount_with_tax(line)
                    unit_price = amount / Decimal(str(line.quantity))
                elif line.product:
                    old_quantity = line.quantity
                    line.quantity = 1.0
                    unit_price = compute_amount_with_tax(line)
                    line.quantity = old_quantity

            # Only compute subtotals if the two fields are provided to speed up

            if currency:
                amount = currency.round(amount)
            amount_w_tax[line.id] = amount
            unit_price_w_tax[line.id] = unit_price

        result = {
            'amount_w_tax': amount_w_tax,
            'unit_price_w_tax': unit_price_w_tax,
        }
        for key in result.keys():
            if key not in names:
                del result[key]
        return result

    @fields.depends('type', 'unit_price', 'quantity', 'taxes', 'sale',
                    '_parent_sale.currency', 'currency', 'product')
    def on_change_with_unit_price_w_tax(self, name=None):
        if not self.sale:
            self.sale = Transaction().context.get('sale')
        return SaleLine.get_price_with_tax(
            [self], ['unit_price_w_tax'])['unit_price_w_tax'][self.id]

    @fields.depends('type', 'unit_price', 'quantity', 'taxes', 'sale',
                    '_parent_sale.currency', 'currency', 'product')
    def on_change_with_amount_w_tax(self, name=None):
        if not self.sale:
            self.sale = Transaction().context.get('sale')
        return SaleLine.get_price_with_tax(
            [self], ['amount_w_tax'])['amount_w_tax'][self.id]
Beispiel #19
0
class Work:
    __name__ = 'project.work'
    predecessors = fields.Many2Many('project.predecessor_successor',
                                    'successor',
                                    'predecessor',
                                    'Predecessors',
                                    domain=[
                                        ('parent', '=', Eval('parent')),
                                        ('id', '!=', Eval('id')),
                                    ],
                                    depends=['parent', 'id'])
    successors = fields.Many2Many('project.predecessor_successor',
                                  'predecessor',
                                  'successor',
                                  'Successors',
                                  domain=[
                                      ('parent', '=', Eval('parent')),
                                      ('id', '!=', Eval('id')),
                                  ],
                                  depends=['parent', 'id'])
    leveling_delay = fields.Float("Leveling Delay", required=True)
    back_leveling_delay = fields.Float("Back Leveling Delay", required=True)
    allocations = fields.One2Many('project.allocation',
                                  'work',
                                  'Allocations',
                                  states={
                                      'invisible': Eval('type') != 'task',
                                  },
                                  depends=['type'])
    duration = fields.Function(
        fields.TimeDelta('Duration', 'company_work_time'),
        'get_function_fields')
    early_start_time = fields.DateTime("Early Start Time", readonly=True)
    late_start_time = fields.DateTime("Late Start Time", readonly=True)
    early_finish_time = fields.DateTime("Early Finish Time", readonly=True)
    late_finish_time = fields.DateTime("Late Finish Time", readonly=True)
    actual_start_time = fields.DateTime("Actual Start Time")
    actual_finish_time = fields.DateTime("Actual Finish Time")
    constraint_start_time = fields.DateTime("Constraint Start Time")
    constraint_finish_time = fields.DateTime("Constraint  Finish Time")
    early_start_date = fields.Function(fields.Date('Early Start'),
                                       'get_function_fields')
    late_start_date = fields.Function(fields.Date('Late Start'),
                                      'get_function_fields')
    early_finish_date = fields.Function(fields.Date('Early Finish'),
                                        'get_function_fields')
    late_finish_date = fields.Function(fields.Date('Late Finish'),
                                       'get_function_fields')
    actual_start_date = fields.Function(fields.Date('Actual Start'),
                                        'get_function_fields',
                                        setter='set_function_fields')
    actual_finish_date = fields.Function(fields.Date('Actual Finish'),
                                         'get_function_fields',
                                         setter='set_function_fields')
    constraint_start_date = fields.Function(fields.Date('Constraint Start',
                                                        depends=['type']),
                                            'get_function_fields',
                                            setter='set_function_fields')
    constraint_finish_date = fields.Function(fields.Date('Constraint Finish',
                                                         depends=['type']),
                                             'get_function_fields',
                                             setter='set_function_fields')

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

    @classmethod
    def validate(cls, works):
        super(Work, cls).validate(works)
        cls.check_recursion(works, parent='successors')

    @staticmethod
    def default_leveling_delay():
        return 0.0

    @staticmethod
    def default_back_leveling_delay():
        return 0.0

    @classmethod
    def get_function_fields(cls, works, names):
        '''
        Function to compute function fields
        '''
        res = {}

        ids = [w.id for w in works]
        if 'duration' in names:
            all_works = cls.search([('parent', 'child_of', ids),
                                    ('active', '=', True)]) + works
            all_works = set(all_works)

            durations = {}
            id2work = {}
            leafs = set()
            for work in all_works:
                id2work[work.id] = work
                if not work.children:
                    leafs.add(work.id)

                total_allocation = 0
                if not work.allocations or not work.effort_duration:
                    durations[work.id] = work.effort_duration
                    continue
                for allocation in work.allocations:
                    total_allocation += allocation.percentage
                durations[work.id] = datetime.timedelta(
                    seconds=work.effort_duration.total_seconds() /
                    (total_allocation / 100.0))

            while leafs:
                for work_id in leafs:
                    work = id2work[work_id]
                    all_works.remove(work)
                    if not work.active:
                        continue
                    if work.parent and work.parent.id in durations:
                        durations[work.parent.id] += durations[work_id]
                next_leafs = set(w.id for w in all_works)
                for work in all_works:
                    if not work.parent:
                        continue
                    if work.parent.id in next_leafs and work.parent in works:
                        next_leafs.remove(work.parent.id)
                leafs = next_leafs
            res['duration'] = durations

        fun_fields = ('early_start_date', 'early_finish_date',
                      'late_start_date', 'late_finish_date',
                      'actual_start_date', 'actual_finish_date',
                      'constraint_start_date', 'constraint_finish_date')
        db_fields = ('early_start_time', 'early_finish_time',
                     'late_start_time', 'late_finish_time',
                     'actual_start_time', 'actual_finish_time',
                     'constraint_start_time', 'constraint_finish_time')

        for fun_field, db_field in zip(fun_fields, db_fields):
            if fun_field in names:
                values = {}
                for work in works:
                    values[work.id] = getattr(work, db_field) \
                        and getattr(work, db_field).date() or None
                res[fun_field] = values

        return res

    @classmethod
    def set_function_fields(cls, works, name, value):
        fun_fields = ('actual_start_date', 'actual_finish_date',
                      'constraint_start_date', 'constraint_finish_date')
        db_fields = ('actual_start_time', 'actual_finish_time',
                     'constraint_start_time', 'constraint_finish_time')
        for fun_field, db_field in zip(fun_fields, db_fields):
            if fun_field == name:
                cls.write(
                    works, {
                        db_field:
                        (value
                         and datetime.datetime.combine(value, datetime.time())
                         or None),
                    })
                break

    @property
    def hours(self):
        if not self.duration:
            return 0
        return self.duration.total_seconds() / 60 / 60

    @classmethod
    def add_minutes(cls, company, date, minutes):
        minutes = int(round(minutes))
        minutes = date.minute + minutes

        hours = minutes // 60
        if hours:
            date = cls.add_hours(company, date, hours)

        minutes = minutes % 60

        date = datetime.datetime(date.year, date.month, date.day, date.hour,
                                 minutes, date.second)

        return date

    @classmethod
    def add_hours(cls, company, date, hours):
        while hours:
            if hours != intfloor(hours):
                minutes = (hours - intfloor(hours)) * 60
                date = cls.add_minutes(company, date, minutes)
            hours = intfloor(hours)

            hours = date.hour + hours
            days = hours // company.hours_per_work_day
            if days:
                date = cls.add_days(company, date, days)

            hours = hours % company.hours_per_work_day

            date = datetime.datetime(date.year, date.month, date.day,
                                     intfloor(hours), date.minute, date.second)

            hours = hours - intfloor(hours)

        return date

    @classmethod
    def add_days(cls, company, date, days):
        day_per_week = company.hours_per_work_week / company.hours_per_work_day

        while days:
            if days != intfloor(days):
                hours = (days - intfloor(days)) * company.hours_per_work_day
                date = cls.add_hours(company, date, hours)
            days = intfloor(days)

            days = date.weekday() + days

            weeks = days // day_per_week
            days = days % day_per_week

            if weeks:
                date = cls.add_weeks(company, date, weeks)

            date += datetime.timedelta(days=-date.weekday() + intfloor(days))

            days = days - intfloor(days)

        return date

    @classmethod
    def add_weeks(cls, company, date, weeks):
        day_per_week = company.hours_per_work_week / company.hours_per_work_day

        if weeks != intfloor(weeks):
            days = (weeks - intfloor(weeks)) * day_per_week
            if days:
                date = cls.add_days(company, date, days)

        date += datetime.timedelta(days=7 * intfloor(weeks))

        return date

    def compute_dates(self):
        values = {}
        get_early_finish = lambda work: values.get(work, {}).get(
            'early_finish_time', work.early_finish_time)
        get_late_start = lambda work: values.get(work, {}).get(
            'late_start_time', work.late_start_time)
        maxdate = lambda x, y: x and y and max(x, y) or x or y
        mindate = lambda x, y: x and y and min(x, y) or x or y

        # propagate constraint_start_time
        constraint_start = reduce(maxdate, (pred.early_finish_time
                                            for pred in self.predecessors),
                                  None)

        if constraint_start is None and self.parent:
            constraint_start = self.parent.early_start_time

        constraint_start = maxdate(constraint_start,
                                   self.constraint_start_time)

        works = deque([(self, constraint_start)])
        work2children = {}
        parent = None

        while works or parent:
            if parent:
                work = parent
                parent = None

                # Compute early_finish
                if work.children:
                    early_finish_time = reduce(
                        maxdate, map(get_early_finish, work.children), None)
                else:
                    early_finish_time = None
                    if values[work]['early_start_time']:
                        early_finish_time = self.add_hours(
                            work.company, values[work]['early_start_time'],
                            work.hours)
                values[work]['early_finish_time'] = early_finish_time

                # Propagate constraint_start on successors
                for w in work.successors:
                    works.append((w, early_finish_time))

                if not work.parent:
                    continue

                # housecleaning work2children
                if work.parent not in work2children:
                    work2children[work.parent] = set()
                work2children[work.parent].update(work.successors)

                if work in work2children[work.parent]:
                    work2children[work.parent].remove(work)

                # if no sibling continue to walk up the tree
                if not work2children.get(work.parent):
                    if work.parent not in values:
                        values[work.parent] = {}
                    parent = work.parent

                continue

            work, constraint_start = works.popleft()
            # take constraint define on the work into account
            constraint_start = maxdate(constraint_start,
                                       work.constraint_start_time)

            if constraint_start:
                early_start = self.add_hours(work.company, constraint_start,
                                             work.leveling_delay)
            else:
                early_start = None

            # update values
            if work not in values:
                values[work] = {}
            values[work]['early_start_time'] = early_start

            # Loop on children if they exist
            if work.children and work not in work2children:
                work2children[work] = set(work.children)
                # Propagate constraint_start on children
                for w in work.children:
                    if w.predecessors:
                        continue
                    works.append((w, early_start))
            else:
                parent = work

        # propagate constraint_finish_time
        constraint_finish = reduce(mindate, (succ.late_start_time
                                             for succ in self.successors),
                                   None)

        if constraint_finish is None and self.parent:
            constraint_finish = self.parent.late_finish_time

        constraint_finish = mindate(constraint_finish,
                                    self.constraint_finish_time)

        works = deque([(self, constraint_finish)])
        work2children = {}
        parent = None

        while works or parent:
            if parent:
                work = parent
                parent = None

                # Compute late_start
                if work.children:
                    reduce(mindate, map(get_late_start, work.children), None)
                else:
                    late_start_time = None
                    if values[work]['late_finish_time']:
                        late_start_time = self.add_hours(
                            work.company, values[work]['late_finish_time'],
                            -work.hours)
                values[work]['late_start_time'] = late_start_time

                # Propagate constraint_finish on predecessors
                for w in work.predecessors:
                    works.append((w, late_start_time))

                if not work.parent:
                    continue

                # housecleaning work2children
                if work.parent not in work2children:
                    work2children[work.parent] = set()
                work2children[work.parent].update(work.predecessors)

                if work in work2children[work.parent]:
                    work2children[work.parent].remove(work)

                # if no sibling continue to walk up the tree
                if not work2children.get(work.parent):
                    if work.parent not in values:
                        values[work.parent] = {}
                    parent = work.parent

                continue

            work, constraint_finish = works.popleft()
            # take constraint define on the work into account
            constraint_finish = mindate(constraint_finish,
                                        work.constraint_finish_time)

            if constraint_finish:
                late_finish = self.add_hours(work.company, constraint_finish,
                                             -work.back_leveling_delay)
            else:
                late_finish = None

            # update values
            if work not in values:
                values[work] = {}
            values[work]['late_finish_time'] = late_finish

            # Loop on children if they exist
            if work.children and work not in work2children:
                work2children[work] = set(work.children)
                # Propagate constraint_start on children
                for w in work.children:
                    if w.successors:
                        continue
                    works.append((w, late_finish))
            else:
                parent = work

        # write values
        write_fields = ('early_start_time', 'early_finish_time',
                        'late_start_time', 'late_finish_time')
        for work, val in values.iteritems():
            write_cond = False
            for field in write_fields:
                if field in val and getattr(work, field) != val[field]:
                    write_cond = True
                    break

            if write_cond:
                self.write([work], val)

    def reset_leveling(self):
        get_key = lambda w: (set(p.id for p in w.predecessors),
                             set(s.id for s in w.successors))

        parent_id = self.parent and self.parent.id or None
        siblings = self.search([('parent', '=', parent_id)])
        to_clean = []

        ref_key = get_key(self)
        for sibling in siblings:
            if sibling.leveling_delay == sibling.back_leveling_delay == 0:
                continue
            if get_key(sibling) == ref_key:
                to_clean.append(sibling)

        if to_clean:
            self.write(to_clean, {
                'leveling_delay': 0,
                'back_leveling_delay': 0,
            })

    def create_leveling(self):
        # define some helper functions
        get_key = lambda w: (set(p.id for p in w.predecessors),
                             set(s.id for s in w.successors))
        over_alloc = lambda current_alloc, work: (reduce(
            lambda res, alloc:
            (res or (current_alloc[alloc.employee.id] + alloc.percentage) > 100
             ), work.allocations, False))

        def sum_allocs(current_alloc, work):
            res = defaultdict(float)
            for alloc in work.allocations:
                empl = alloc.employee.id
                res[empl] = current_alloc[empl] + alloc.percentage
            return res

        def compute_delays(siblings):
            # time_line is a list [[end_delay, allocations], ...], this
            # mean that allocations is valid between the preceding end_delay
            # (or 0 if it doesn't exist) and the current end_delay.
            timeline = []
            for sibling in siblings:
                delay = 0
                ignored = []
                overloaded = []
                item = None

                while timeline:
                    # item is [end_delay, allocations]
                    item = heappop(timeline)
                    if over_alloc(item[1], sibling):
                        ignored.extend(overloaded)
                        ignored.append(item)
                        delay = item[0]
                        continue
                    elif item[1] >= delay + sibling.duration:
                        overloaded.append(item)
                    else:
                        # Succes!
                        break

                heappush(timeline, [
                    delay + sibling.duration,
                    sum_allocs(defaultdict(float), sibling), sibling.id
                ])

                for i in ignored:
                    heappush(timeline, i)
                for i in overloaded:
                    i[1] = sum_allocs(i[1], sibling)
                    heappush(timeline, i)

                yield sibling, delay

        siblings = self.search([('parent', '=',
                                 self.parent.id if self.parent else None)])

        refkey = get_key(self)
        siblings = [s for s in siblings if get_key(s) == refkey]

        for sibling, delay in compute_delays(siblings):
            self.write([sibling], {
                'leveling_delay': delay,
            })

        siblings.reverse()
        for sibling, delay in compute_delays(siblings):
            self.write([sibling], {
                'back_leveling_delay': delay,
            })

        if self.parent:
            self.parent.compute_dates()

    @classmethod
    def write(cls, *args):
        super(Work, cls).write(*args)

        actions = iter(args)
        for works, values in zip(actions, actions):
            if 'effort' in values:
                for work in works:
                    work.reset_leveling()
            fields = ('constraint_start_time', 'constraint_finish_time',
                      'effort')
            if reduce(lambda x, y: x or y in values, fields, False):
                for work in works:
                    work.compute_dates()

    @classmethod
    def create(cls, vlist):
        works = super(Work, cls).create(vlist)
        for work in works:
            work.reset_leveling()
            work.compute_dates()
        return works

    @classmethod
    def delete(cls, works):
        to_update = set()
        for work in works:
            if work.parent and work.parent not in works:
                to_update.add(work.parent)
                to_update.update(c for c in work.parent.children
                                 if c not in works)
        super(Work, cls).delete(works)

        for work in to_update:
            work.reset_leveling()
            work.compute_dates()
Beispiel #20
0
class QuotationLine(ModelSQL, ModelView):
    "Purchase Request For Quotation Line"
    __name__ = 'purchase.request.quotation.line'

    supplier = fields.Function(fields.Many2One('party.party', 'Supplier'),
                               'get_supplier')
    supply_date = fields.Date('Supply Date',
                              help="When it should be delivered.")
    product = fields.Function(fields.Many2One('product.product', 'Product'),
                              'get_product',
                              searcher='search_product')
    description = fields.Text('Description',
                              states={'required': ~Eval('product')},
                              depends=['product'])
    quantity = fields.Float('Quantity',
                            digits=(16, Eval('unit_digits', 2)),
                            required=True,
                            depends=['unit_digits'])
    unit = fields.Many2One(
        'product.uom',
        'Unit',
        ondelete='RESTRICT',
        states={
            'required': Bool(Eval('product')),
        },
        domain=[
            If(Bool(Eval('product_uom_category')),
               ('category', '=', Eval('product_uom_category')),
               ('category', '!=', -1)),
        ],
        depends=['product', 'product_uom_category'])
    unit_digits = fields.Function(fields.Integer('Unit Digits'),
                                  'on_change_with_unit_digits')
    product_uom_category = fields.Function(
        fields.Many2One('product.uom.category', 'Product Uom Category'),
        'on_change_with_product_uom_category')
    unit_price = fields.Numeric('Unit Price', digits=price_digits)
    currency = fields.Many2One('currency.currency',
                               'Currency',
                               states={
                                   'required': Bool(Eval('unit_price')),
                               },
                               depends=['unit_price'])
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
                                      'on_change_with_currency_digits')
    request = fields.Many2One(
        'purchase.request',
        'Request',
        ondelete='CASCADE',
        select=True,
        required=True,
        domain=[
            If(
                Eval('quotation_state') == 'draft',
                ('state', 'in', ['draft', 'quotation', 'received']),
                (),
            ),
        ],
        states={'readonly': Eval('quotation_state') != 'draft'},
        depends=['quotation_state'],
        help="The request which this line belongs to.")
    quotation = fields.Many2One('purchase.request.quotation',
                                'Quotation',
                                ondelete='CASCADE',
                                required=True,
                                domain=[
                                    ('supplier', '=', Eval('supplier')),
                                ],
                                depends=['supplier'])
    quotation_state = fields.Function(fields.Selection(
        'get_quotation_state', 'Request for Quotation State'),
                                      'on_change_with_quotation_state',
                                      searcher='search_quotation_state')

    @staticmethod
    def order_quotation_state(tables):
        pool = Pool()
        Quotation = pool.get('purchase.request.quotation')
        quotation_line, _ = tables[None]
        quotation = Quotation.__table__()
        tables['purchase.request.quotation'] = {
            None: (quotation, quotation_line.quotation == quotation.id),
        }
        return [
            Case((quotation.state == 'received', 0), else_=1), quotation.state
        ]

    def get_supplier(self, name):
        if self.quotation and self.quotation.supplier:
            return self.quotation.supplier.id

    @fields.depends('request')
    def on_change_request(self):
        if self.request:
            self.product = self.request.product
            self.description = self.request.description
            self.quantity = self.request.quantity
            self.unit = self.request.uom
            self.currency = self.request.currency
            self.supply_date = self.request.supply_date or datetime.date.max

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

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

    @fields.depends('currency')
    def on_change_with_currency_digits(self, name=None):
        if self.currency:
            return self.currency.digits
        return None

    @classmethod
    def get_quotation_state(cls):
        pool = Pool()
        Quotation = pool.get('purchase.request.quotation')
        return (Quotation.fields_get(['state'])['state']['selection'])

    @fields.depends('quotation', '_parent_quotation.state')
    def on_change_with_quotation_state(self, name=None):
        pool = Pool()
        Quotation = pool.get('purchase.request.quotation')
        if self.quotation:
            return self.quotation.state
        return Quotation.default_state()

    @classmethod
    def search_quotation_state(cls, name, clause):
        return [('quotation.state', ) + tuple(clause[1:])]

    def get_rec_name(self, name):
        return '%s - %s' % (self.quotation.rec_name, self.supplier.rec_name)

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

    @classmethod
    def delete(cls, quotationlines):
        pool = Pool()
        Request = pool.get('purchase.request')
        requests = [l.request for l in quotationlines]
        super(QuotationLine, cls).delete(quotationlines)
        Request.update_state(requests)

    def get_product(self, name):
        if self.request and self.request.product:
            return self.request.product.id

    @classmethod
    def search_product(cls, name, clause):
        return [('request.' + clause[0], ) + tuple(clause[1:])]
Beispiel #21
0
class InvestmentDeclaration(ModelSQL, ModelView):
    """ Investment Declaration"""

    __name__ = 'investment.declaration'

    name = fields.Char('Reference', help='Enter Reference')
    salary_code = fields.Char('Salary Code')
    employee = fields.Many2One('company.employee', 'Employee')
    gender = fields.Selection([('male', 'Male'), ('female', 'Female'),
                               ('others', 'Others'), ('None', '')],
                              'Gender',
                              help='Select Gender')
    pan_number = fields.Char('PAN No.')
    date_of_joining = fields.Date('Date of Joining')
    dob = fields.Date('Date of Birth')
    fiscal_year = fields.Many2One('account.fiscalyear', 'Fiscal Year')
    start_date = fields.Date('Starting Date')
    end_date = fields.Date('Ending Date')
    income_tax_assessment_year = fields.Char('Income Tax assessment year')
    declaration_lines = fields.One2Many(
        'investment_declaration.line',
        'declaration',
        'Declarations',
        help='Fill the Investment Declarations')
    income_declaration_lines = fields.One2Many(
        'hr.payroll.income.decalartion',
        'declaration',
        'Income Declaration',
    )
    total_income_declared = fields.Function(fields.Float('Total'),
                                            'getter_amount')
    net_tax_exempted = fields.Float('Total Tax Exempted',
                                    help="Under Chapter VI-A of IT Act")
    state = fields.Selection(
        [('draft', 'Draft'), ('awaiting_approval', 'Awaiting Approval'),
         ('approved', 'Approved'),
         ('awaiting_final_declaration', 'Awaiting Final Declaration'),
         ('closed', 'Closed')],
        'Status',
        readonly=True,
        sort=False)
    # TODO: Review the state field

    @staticmethod
    def default_state():
        '''returns draft state as default value'''
        return 'draft'

    @staticmethod
    def default_fiscal_year():
        '''returns Current fiscal year as default value'''
        pool = Pool()
        fiscal = pool.get('account.fiscalyear')
        current_date = date.today()
        current_fiscal_year = fiscal.search(
            [('start_date', '<=', current_date),
             ('end_date', '>=', current_date)],
            limit=1)
        if len(current_fiscal_year) == 1:
            return current_fiscal_year[0].id

    @classmethod
    def getter_amount(cls, incomes, names):
        total_income = {}
        income_total = 0
        for income in incomes:
            for income_line in income.income_declaration_lines:
                income_total += income_line.total_amount
            total_income[income.id] = income_total
        result = {
            'total_income_declared': total_income,
        }
        return result

    @staticmethod
    def default_start_date():
        '''
        returns start_date as this year's current
        fiscal year's start_date value.
        '''
        pool = Pool()
        fiscal = pool.get('account.fiscalyear')
        current_date = date.today()
        current_fiscal_year = fiscal.search(
            [('start_date', '<=', current_date),
             ('end_date', '>=', current_date)],
            limit=1)
        return current_fiscal_year[0].start_date

    @staticmethod
    def default_end_date():
        '''
        returns end_date as this year's current
        fiscal year's end_date value.
        '''
        pool = Pool()
        fiscal = pool.get('account.fiscalyear')
        current_date = date.today()
        current_fiscal_year = fiscal.search(
            [('start_date', '<=', current_date),
             ('end_date', '>=', current_date)],
            limit=1)
        return current_fiscal_year[0].end_date

    @fields.depends('fiscal_year')
    def on_change_fiscal_year(self, name=None):
        if self.fiscal_year:
            self.income_tax_assessment_year = (
                self.fiscal_year.income_tax_assessment_year)

    @staticmethod
    def default_employee():
        ''' returns the logged in employee'''
        pool = Pool()
        User = pool.get('res.user')
        user = User(Transaction().user)
        employee = user.employee
        return employee.id if employee else None

    @fields.depends('employee')
    def on_change_employee(self):
        ''' 
        Auto filling of fields
        based on employee and fiscal year
        '''
        if self.employee:
            self.salary_code = self.employee.salary_code
            self.pan_number = self.employee.pan_number
            self.date_of_joining = self.employee.date_of_joining
            # self.gender=self.employee.gender
            # self.dob=self.employee.dob

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._buttons.update({
            'calculate_net_tax_exempted': {},
        })

    @classmethod
    def calculate_net_tax_exempted(cls, investments):
        '''Calculate Net tax exempted
        Add the section wise total amount
        consider the section wise amount and
        find the maximum rebate allowed 
        '''
        investment_total = 0
        dict_section_amount = {}
        for investment in investments:
            for line in investment.declaration_lines:
                if line.section not in dict_section_amount:
                    dict_section_amount[line.section] = line.actual_amount
                else:
                    dict_section_amount[line.section] += line.actual_amount
            for key, value in dict_section_amount.items():
                investment_total += min([key.allowed_limit, value])
            investment.net_tax_exempted = investment_total
            investment.save()
Beispiel #22
0
class ImportDataFloat(ModelSQL):
    "Import Data Float"
    __name__ = 'test.import_data.float'
    float = fields.Float('Float')
class Routing(metaclass=PoolMeta):
    __name__ = 'production.routing'

    supplier = fields.Many2One(
        'party.party', "Supplier",
        help="The supplier to outsource the production.")
    supplier_service = fields.Many2One(
        'product.product', "Service",
        ondelete='RESTRICT',
        domain=[
            ('purchasable', '=', True),
            ('template.type', '=', 'service'),
            ],
        states={
            'required': Bool(Eval('supplier')),
            'invisible': ~Eval('supplier'),
            },
        depends=['supplier', 'supplier_service_supplier'],
        help="The service to buy to the supplier for the production.")
    supplier_service_supplier = fields.Many2One(
        'purchase.product_supplier', "Supplier's Service",
        ondelete='RESTRICT',
        domain=[
            ('template.type', '=', 'service'),
            If(Bool('supplier_service'),
                ['OR',
                    [
                        ('template.products', '=', Eval('supplier_service')),
                        ('product', '=', None),
                        ],
                    ('product', '=', Eval('supplier_service')),
                    ],
                []),
            ('party', '=', Eval('supplier', -1)),
            ],
        states={
            'invisible': ~Eval('supplier'),
            },
        depends=['supplier_service', 'supplier'],
        help="The supplier's service to buy for the production.")
    supplier_quantity = fields.Float("Quantity",
        states={
            'invisible': ~Eval('supplier_service'),
            'required': Bool(Eval('supplier_service')),
            },
        depends=['supplier_service'],
        help="The quantity to buy to produce one time the BOM.")

    @classmethod
    def default_supplier_quantity(cls):
        return 1

    @fields.depends('supplier')
    def _get_product_supplier_pattern(self):
        return {
            'party': self.supplier.id if self.supplier else -1,
            }

    @fields.depends('supplier', 'supplier_service',
        'supplier_service_supplier')
    def on_change_supplier_service(self):
        if self.supplier_service:
            product_suppliers = list(
                self.supplier_service.product_suppliers_used(
                    **self._get_product_supplier_pattern()))
            if len(product_suppliers) == 1:
                self.supplier_service_supplier, = product_suppliers
            elif (self.supplier_service_supplier
                    and (self.supplier_service_supplier
                        not in product_suppliers)):
                self.supplier_service = None

    @fields.depends('supplier_service', 'supplier_service_supplier')
    def on_change_supplier_service_supplier(self):
        if self.supplier_service_supplier:
            if self.supplier_service_supplier.product:
                self.supplier_service = self.supplier_service_supplier.product
            elif not self.supplier_service:
                products = self.supplier_service_supplier.template.products
                if len(products) == 1:
                    self.supplier_service, = products

    @classmethod
    def view_attributes(cls):
        return super(Routing, cls).view_attributes() + [
            ('//page[@id="supplier"]', 'states', {
                    'invisible': ~Eval('supplier'),
                    }),
            ]
Beispiel #24
0
class ImportDataFloatRequired(ModelSQL):
    "Import Data Float Required"
    __name__ = 'test.import_data.float_required'
    float = fields.Float('Float', required=True)
Beispiel #25
0
class ShipmentCarrierMixin(PackageMixin):
    """
    Mixin class which implements all the fields and methods required for
    getting shipping rates and generating labels
    """

    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.Many2One(
        'shipment.tracking',
        'Tracking Number',
        select=True,
        states={'readonly': Eval('state') == 'done'},
        depends=['state'])

    shipping_instructions = fields.Text('Shipping Instructions',
                                        states={
                                            'readonly':
                                            Eval('state').in_(
                                                ['cancel', 'done']),
                                        },
                                        depends=['state'])

    available_carrier_services = fields.Function(
        fields.One2Many("carrier.service", None, "Available Carrier Services"),
        getter="on_change_with_available_carrier_services")
    carrier_service = fields.Many2One(
        "carrier.service",
        "Carrier Service",
        domain=[('id', 'in', Eval('available_carrier_services'))],
        depends=['available_carrier_services', 'state'])
    carrier_cost_method = fields.Function(
        fields.Char('Carrier Cost Method'),
        "on_change_with_carrier_cost_method")
    shipping_manifest = fields.Many2One("shipping.manifest",
                                        "Shipping Manifest",
                                        readonly=True,
                                        select=True)

    @property
    def carrier_cost_moves(self):
        "Moves to use for carrier cost calculation"
        return []

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

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

    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.carrier_cost_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(ShipmentCarrierMixin, cls).__setup__()
        cls.carrier_service.states = {
            'readonly': Eval('state') == 'done',
        }
        cls._buttons.update({
            'label_wizard': {
                'invisible':
                Or((~Eval('state').in_(['packed', 'done'])),
                   (Bool(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',
        })

        # Following fields are already there in customer shipment, have
        # them in mixin so other shipment model can also use it.
        cls.carrier = fields.Many2One('carrier',
                                      'Carrier',
                                      states={
                                          'readonly': Eval('state') == 'done',
                                      },
                                      depends=['state'])
        cls.cost_currency = fields.Many2One('currency.currency',
                                            'Cost Currency',
                                            states={
                                                'invisible':
                                                ~Eval('carrier'),
                                                'required':
                                                Bool(Eval('carrier')),
                                                'readonly':
                                                Eval('state') == 'done',
                                            },
                                            depends=['carrier', 'state'])
        cls.cost_currency_digits = fields.Function(
            fields.Integer('Cost Currency Digits'),
            'on_change_with_cost_currency_digits')
        cls.cost = fields.Numeric(
            'Cost',
            digits=(16, Eval('cost_currency_digits', 2)),
            states={
                'invisible': ~Eval('carrier'),
                'readonly': Eval('state') == 'done',
            },
            depends=['carrier', 'state', 'cost_currency_digits'])

    @fields.depends('currency')
    def on_change_with_cost_currency_digits(self, name=None):
        if self.cost_currency:
            return self.cost_currency.digits
        return 2

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

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

    @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(silent=True)
        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 shipment
        """
        ModelData = Pool().get('ir.model.data')

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

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

    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

    def _create_default_package(self, box_type=None):
        """
        Create a single stock package for the whole shipment
        """
        Package = Pool().get('stock.package')

        package, = Package.create([{
            'shipment':
            '%s,%d' % (self.__name__, self.id),
            'box_type':
            box_type and box_type.id,
            'moves': [('add', self.carrier_cost_moves)],
        }])
        return package

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

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

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

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

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

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

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

        return []

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

            {
                'display_name': Name to display,
                'carrier_service': carrier.service active record,
                'cost': cost,
                'cost_currency': currency.currency active repord,
                'carrier': carrier active record,
            }
        """
        Currency = Pool().get('currency.currency')

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

        self.cost = shipment_cost
        self.cost_currency = self.cost_currency
        self.carrier = rate['carrier']
        self.carrier_service = rate['carrier_service']
        self.save()

    def generate_shipping_labels(self, **kwargs):
        """
        Generates shipment label for shipment and saves labels,
        tracking numbers.
        """
        self.raise_user_error(
            "Shipping label generation feature is not available")

    @staticmethod
    def default_cost_currency():
        Company = Pool().get('company.company')

        company_id = Transaction().context.get('company')

        return company_id and Company(company_id).currency.id or None

    @property
    def ship_from_address(self):
        return None

    @property
    def ship_to_address(self):
        return None
Beispiel #26
0
class ChildernEductionAllowance(Workflow, ModelSQL, ModelView):
    """Childern Eduction Allowance for an Employee"""

    __name__ = 'hr.allowance.cea'

    salary_code = fields.Char('Salary Code', required=True)
    employee = fields.Many2One('company.employee',
                               'Employee Name',
                               required=True)
    designation = fields.Many2One('employee.designation',
                                  'Designation',
                                  required=True)
    department = fields.Many2One('company.department',
                                 'Department',
                                 required=True)
    children_no = fields.Selection([
        ('1', 'One'),
        ('2', 'Two'),
    ],
                                   'Number of Children',
                                   required=True)
    amount = fields.Function(fields.Float('Education Amount', required=True),
                             'on_change_with_amount')
    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'])
    state = fields.Selection([
        ('draft', 'Draft'),
        ('submit', 'Submit'),
        ('cash_section_officer', 'Cash Section_officer'),
        ('cancel', 'Cancel'),
        ('approve', 'Approve'),
    ],
                             'Status',
                             readonly=True)

    @staticmethod
    def default_state():
        return 'draft'

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

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._buttons.update({
            'submit': {
                'invisible': ~Eval('state').in_(['draft'])
            },
            '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 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

    @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

    @fields.depends('children_no')
    def on_change_with_amount(self, name=None):
        res = 0
        if self.children_no:
            if (self.children_no == '1'):
                res = 2250
            elif (self.children_no == '2'):
                res = 4500
        return res

    @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
Beispiel #27
0
class WorkStatus(DeactivableMixin, sequence_ordered(), ModelSQL, ModelView):
    'Work Status'
    __name__ = 'project.work.status'

    _get_default_status_cache = Cache('project_work_status.get_default_status')
    _get_window_domains_cache = Cache('project_work_status.get_window_domains')

    types = fields.MultiSelection(
        'get_types', "Types",
        help="The type of works which can use this status.")
    name = fields.Char("Name", required=True, translate=True)
    progress = fields.Float(
        "Progress",
        domain=['OR',
            ('progress', '=', None),
            [
                ('progress', '>=', 0),
                ('progress', '<=', 1),
                ],
            ],
        help="The minimum progress required for this status.")
    default = fields.Boolean(
        "Default",
        help="Check to use as default status for the type.")
    count = fields.Boolean(
        "Count",
        help="Check to show the number of works in this status.")

    @classmethod
    def get_types(cls):
        pool = Pool()
        Work = pool.get('project.work')
        return Work.fields_get(['type'])['type']['selection']

    @classmethod
    def get_default_status(cls, type_=None):
        if type_ is None:
            return None
        status = cls._get_default_status_cache.get(type_, -1)
        if status != -1:
            return status
        records = cls.search([
                ('types', 'in', type_),
                ('default', '=', True)
                ], limit=1)
        if records:
            status = records[0].id
        else:
            status = None
        cls._get_default_status_cache.set(type, status)
        return status

    @classmethod
    def create(cls, vlist):
        cls._get_default_status_cache.clear()
        cls._get_window_domains_cache.clear()
        return super().create(vlist)

    @classmethod
    def write(cls, *args):
        super().write(*args)
        cls._get_default_status_cache.clear()
        cls._get_window_domains_cache.clear()

    @classmethod
    def delete(cls, status):
        cls._get_default_status_cache.clear()
        cls._get_window_domains_cache.clear()
        super().delete(status)

    @classmethod
    def get_window_domains(cls, action):
        pool = Pool()
        Data = pool.get('ir.model.data')
        if action.id == Data.get_id('project', 'act_project_tree'):
            return cls._get_window_domains([x[0] for x in cls.get_types()])
        elif action.id == Data.get_id('project', 'act_project_form'):
            return cls._get_window_domains(['project'])
        elif action.id == Data.get_id('project', 'act_task_form'):
            return cls._get_window_domains(['task'])
        else:
            return []

    @classmethod
    def _get_window_domains(cls, types):
        key = tuple(sorted(types))
        domains = cls._get_window_domains_cache.get(key)
        if domains is not None:
            return domains
        encoder = PYSONEncoder()
        domains = []
        for status in cls.search([('types', 'in', types)]):
            domain = encoder.encode([('status', '=', status.id)])
            domains.append((status.name, domain, status.count))
        if domains:
            domains.append(
                (gettext('project.msg_domain_all'), '[]', False))
        cls._get_window_domains_cache.set(key, domains)
        return domains
Beispiel #28
0
class EstateAllotment(Workflow, ModelSQL, ModelView):
    """Employee Allotment"""

    __name__ = "estate.allotment"

    employee = fields.Many2One('company.employee', 'Employee', states={
        'readonly': ~Eval('state').in_(['draft'])
    }, depends=['state'])
    center_name = fields.Function(
        fields.Char('Center Name'),
        'on_change_with_center_name'
    )
    department_name = fields.Function(
        fields.Char('Department Name'),
        'on_change_with_departemnt_name'
    )
    salary_code = fields.Function(
        fields.Char('Salary Code'),
        'on_change_with_salary_code'
    )
    quarter_no = fields.Char('Quarter No.', states={
        'readonly': ~Eval('state').in_(['draft'])
    }, depends=['state'])
    location = fields.Selection(
        [
            ('ansari_nagar_east', 'Ansari Nagar(E)'),
            ('ansari_nagar_west', 'Ansari Nagar(W)'),
            ('masjid_moth', 'Masjid Moth'),
            ('a_v_nagar_old', 'Ayurvigyan Nagar(Old)'),
            ('a_v_nagar_new', 'Ayurvigyan Nagar(New)'),
            ('asiad_village_complex', 'Asiad Village Complex'),
            ('ballabhgarh', 'Ballabhgarh'),
        ], 'Location', states={
            'readonly': ~Eval('state').in_(['draft'])
        }, depends=['state'])
    quarter_type = fields.Many2One(
        'estate.quarter_type',
        'Quarter Type',
        states={
            'readonly': ~Eval('state').in_(['draft'])
        },
        depends=['state', 'location'],
        domain=[
            ('locations', '=', Eval('location'))
        ])
    state = fields.Selection([
        ('draft', 'Draft'),
        ('confirm', 'Confirm'),
        ('submit', 'Submit')
    ], 'Status', required=True, readonly=True)
    garage = fields.Boolean(
        'Garage?',
        states={
            'readonly': ~Eval('state').in_(['draft'])
        },
        depends=['state'])
    servant_quarter = fields.Boolean(
        'Servant Quarter?',
        states={
            'readonly': ~Eval('state').in_(['draft'])
        },
        depends=['state'])
    date_of_allocation = fields.Date('Date of Allocation', states={
        'readonly': ~Eval('state').in_(['draft'])
    }, depends=['state'])
    date_of_occupation = fields.Date('Date of Occupation', states={
        'readonly': ~Eval('state').in_(['draft'])
    }, depends=['state'])
    date_of_vacation = fields.Date('Date of Vacation', states={
        'readonly': ~Eval('state').in_(['draft'])
    }, depends=['state'])
    license_fee = fields.Function(
        fields.Float('License Fee'),
        'get_license_fee'
    )
    water_charges = fields.Function(
        fields.Float('Water Charges'),
        'get_water_charges'
    )
    garage_fee = fields.Function(
        fields.Float('Garage'),
        'on_change_with_garage_fee'
    )
    servant_quarter_fee = fields.Function(
        fields.Float('Servant Quarter'),
        'on_change_with_servant_quarter_fee'
    )


    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def default_location():
        return 'ansari_nagar_east'

    @staticmethod
    def default_license_fee():
        return 0

    @staticmethod
    def default_water_charges():
        return 0

    @staticmethod
    def default_garage_fee():
        return 0

    @staticmethod
    def default_servant_quarter_fee():
        return 0

    @staticmethod
    def default_quarter_type():
        return 1

    def get_license_fee(self, name):
        if self.quarter_type:
            return self.quarter_type.license_fee
        else:
            return 0
        return self.quarter_type.license_fee    
    
    def get_water_charges(self, name):
        if self.quarter_type:
            return self.quarter_type.water_charges
        else:
            return 0
        return self.quarter_type.water_charges

    @fields.depends('employee')
    def on_change_with_center_name(self, name=None):
        if self.employee and self.employee.center:
                return self.employee.center.name


    @fields.depends('employee')
    def on_change_with_departemnt_name(self, name=None):
        if self.employee and self.employee.department:
                return self.employee.department.name

    @fields.depends('employee')
    def on_change_with_salary_code(self, name=None):
        if self.employee and self.employee.salary_code:
                return self.employee.salary_code
    
    @fields.depends('garage')
    def on_change_with_garage_fee(self, name=None):
        # import pdb;pdb.set_trace()
        garage_fee = 0
        if self.garage:
            garage_fee = 40
        else:
            garage_fee = 0
        return garage_fee

    @fields.depends('servant_quarter')
    def on_change_with_servant_quarter_fee(self, name=None):
        servant_quarter_fee = 0
        if self.servant_quarter:
            servant_quarter_fee = 70
        else:
            servant_quarter_fee = 0
        return servant_quarter_fee  

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

    @classmethod
    # @ModelView.button
    @Workflow.transition('confirm')
    def confirm_details(cls, records):
        print('+++++++++++++++++++++++++++++++++++++')
Beispiel #29
0
class Float(ModelSQL):
    'Float'
    __name__ = 'test.float'
    float = fields.Float(string='Float', help='Test float',
            required=False)
Beispiel #30
0
class SaleOpportunityReportMixin:
    number = fields.Integer('Number')
    converted = fields.Integer('Converted')
    conversion_rate = fields.Function(
        fields.Float('Conversion Rate', digits=(1, 4)), 'get_conversion_rate')
    won = fields.Integer('Won')
    winning_rate = fields.Function(fields.Float('Winning Rate', digits=(1, 4)),
                                   'get_winning_rate')
    lost = fields.Integer('Lost')
    company = fields.Many2One('company.company', 'Company')
    currency = fields.Function(
        fields.Many2One('currency.currency', 'Currency'), 'get_currency')
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
                                      'get_currency_digits')
    amount = fields.Numeric('Amount',
                            digits=(16, Eval('currency_digits', 2)),
                            depends=['currency_digits'])
    converted_amount = fields.Numeric('Converted Amount',
                                      digits=(16, Eval('currency_digits', 2)),
                                      depends=['currency_digits'])
    conversion_amount_rate = fields.Function(
        fields.Float('Conversion Amount Rate', digits=(1, 4)),
        'get_conversion_amount_rate')
    won_amount = fields.Numeric('Won Amount',
                                digits=(16, Eval('currency_digits', 2)),
                                depends=['currency_digits'])
    winning_amount_rate = fields.Function(
        fields.Float('Winning Amount Rate', digits=(1, 4)),
        'get_winning_amount_rate')

    @staticmethod
    def _converted_state():
        return ['converted', 'won']

    @staticmethod
    def _won_state():
        return ['won']

    @staticmethod
    def _lost_state():
        return ['lost']

    def get_conversion_rate(self, name):
        if self.number:
            digits = getattr(self.__class__, name).digits[1]
            return round(float(self.converted) / self.number, digits)
        else:
            return 0.0

    def get_winning_rate(self, name):
        if self.number:
            digits = getattr(self.__class__, name).digits[1]
            return round(float(self.won) / self.number, digits)
        else:
            return 0.0

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

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

    def get_conversion_amount_rate(self, name):
        if self.amount:
            digits = getattr(self.__class__, name).digits[1]
            return round(
                float(self.converted_amount) / float(self.amount), digits)
        else:
            return 0.0

    def get_winning_amount_rate(self, name):
        if self.amount:
            digits = getattr(self.__class__, name).digits[1]
            return round(float(self.won_amount) / float(self.amount), digits)
        else:
            return 0.0

    @classmethod
    def table_query(cls):
        Opportunity = Pool().get('sale.opportunity')
        opportunity = Opportunity.__table__()
        return opportunity.select(
            Max(opportunity.create_uid).as_('create_uid'),
            Max(opportunity.create_date).as_('create_date'),
            Max(opportunity.write_uid).as_('write_uid'),
            Max(opportunity.write_date).as_('write_date'), opportunity.company,
            Count(Literal(1)).as_('number'),
            Sum(
                Case((opportunity.state.in_(
                    cls._converted_state()), Literal(1)),
                     else_=Literal(0))).as_('converted'),
            Sum(
                Case((opportunity.state.in_(cls._won_state()), Literal(1)),
                     else_=Literal(0))).as_('won'),
            Sum(
                Case((opportunity.state.in_(cls._lost_state()), Literal(1)),
                     else_=Literal(0))).as_('lost'),
            Sum(opportunity.amount).as_('amount'),
            Sum(
                Case((opportunity.state.in_(
                    cls._converted_state()), opportunity.amount),
                     else_=Literal(0))).as_('converted_amount'),
            Sum(
                Case((opportunity.state.in_(
                    cls._won_state()), opportunity.amount),
                     else_=Literal(0))).as_('won_amount'))