Beispiel #1
0
class ShipmentOut(metaclass=PoolMeta):
    __name__ = 'stock.shipment.out'
    carrier = fields.Many2One('carrier', 'Carrier', states={
            'readonly': ~Eval('state').in_(['draft', 'waiting', 'assigned',
                    'packed']),
            },
        depends=['state'])
    cost_currency = fields.Many2One('currency.currency',
        'Cost Currency', states={
            'invisible': ~Eval('carrier'),
            'required': Bool(Eval('carrier')),
            'readonly': ~Eval('state').in_(['draft', 'waiting', 'assigned',
                    'packed']),
            }, depends=['carrier', 'state'])
    cost = fields.Numeric('Cost',
        digits=price_digits, states={
            'invisible': ~Eval('carrier'),
            'readonly': ~Eval('state').in_(['draft', 'waiting', 'assigned',
                    'packed']),
            }, depends=['carrier', 'state'])
    cost_invoice_line = fields.Many2One('account.invoice.line',
            'Cost Invoice Line', readonly=True)

    @classmethod
    def __setup__(cls):
        super(ShipmentOut, cls).__setup__()
        cls._error_messages.update({
                'missing_account_revenue': ('Missing "Account Revenue" on '
                    'product "%s".'),
                })

    def _get_carrier_context(self):
        return {}

    def get_carrier_context(self):
        return self._get_carrier_context()

    @fields.depends(methods=['on_change_inventory_moves'])
    def on_change_carrier(self):
        self.on_change_inventory_moves()

    @fields.depends('carrier', 'customer', 'inventory_moves',
        methods=['_get_carrier_context'])
    def on_change_inventory_moves(self):
        try:
            super(ShipmentOut, self).on_change_inventory_moves()
        except AttributeError:
            pass
        if not self.carrier:
            return
        with Transaction().set_context(self._get_carrier_context()):
            cost, currency_id = self.carrier.get_sale_price()
        self.cost = cost.quantize(
            Decimal(1) / 10 ** self.__class__.cost.digits[1])
        self.cost_currency = currency_id

    def _get_cost_tax_rule_pattern(self):
        'Get tax rule pattern for invoice line'
        return {}

    def get_cost_invoice_line(self, invoice):
        pool = Pool()
        Product = pool.get('product.product')
        Currency = pool.get('currency.currency')
        InvoiceLine = pool.get('account.invoice.line')

        if not self.cost:
            return {}
        invoice_line = InvoiceLine()
        product = self.carrier.carrier_product
        invoice_line.type = 'line'
        invoice_line.product = product

        party = invoice.party
        party_context = {}
        if party.lang:
            party_context['language'] = party.lang.code
        with Transaction().set_context(party_context):
            invoice_line.description = Product(product.id).rec_name

        invoice_line.quantity = 1  # XXX
        invoice_line.unit = product.sale_uom.id
        cost = self.cost
        if invoice.currency != self.cost_currency:
            with Transaction().set_context(date=invoice.currency_date):
                cost = Currency.compute(self.cost_currency, cost,
                    invoice.currency, round=False)
        invoice_line.unit_price = cost.quantize(
            Decimal(1) / 10 ** InvoiceLine.unit_price.digits[1])

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

        invoice_line.account = product.account_revenue_used
        if not invoice_line.account:
            self.raise_user_error('missing_account_revenue',
                    error_args=(product.rec_name,))
        return invoice_line
Beispiel #2
0
class EMBEmployeeDetails(Workflow, ModelSQL, ModelView):
    'EMB employee details'

    __name__ = 'emb.employee.details'

    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'])
    date = fields.Date('Dated', states={
            'readonly': ~Eval('state').in_(['draft'])
        },
        depends=['state'])
    bill_month = fields.Char('Embalming Bill for the month of', states={
            'readonly': ~Eval('state').in_(['draft'])
        },
        depends=['state'])
    emb_fee = fields.One2Many(
                        'emb.bill',
                        'bill',
                        'E.M.B Fee', states={
            'readonly': ~Eval('state').in_(['draft'])
        },
        depends=['state']
                    )
    state = fields.Selection([
        ('draft', 'Draft'),
        ('confirm', 'Confirm'),
        ('cancel_emp', 'Cancel'),
        ('f_to_hod', 'Forwarded to HoD'),
        ('f_to_ao', 'Forwarded to AO'),
        ('cancel_hod', 'Cancelled by HoD'),
        ('approve_ao', 'Approved by AO'),
        ('cancel_ao', 'Cancelled by AO')
    ], 'Status', states={
            'readonly': ~Eval('state').in_(['draft'])
        },
        depends=['state'], readonly=True)
    remarks = fields.Char('Remarks', states={
        'required': Eval('state').in_(['cancel_ao']),
        'invisible': ~Eval('state').in_(['cancel_ao'])
    })

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls._transitions |= set((
        ('draft', 'confirm'),
        ('confirm', 'f_to_hod'),
        ('cancel', 'cancel_emp'),
        ('f_to_hod', 'f_to_ao'),
        ('f_to_hod', 'cancel_hod'),
        ('f_to_ao', 'approve_ao'),
        ('f_to_ao', 'cancel_ao'),
        ))
        cls._buttons.update({
            'submit_to_hod': {
                'invisible':~Eval('state').in_(
                    ['draft']),
                'depends':['state'],
                },
            'confirm': {
                'invisible':~Eval('state').in_(
                    ['draft']),
                'depends':['state'],
                }, 
            'cancel': {
                'invisible':~Eval('state').in_(
                    ['confirm']),
                'depends':['state'],
                },     
            'cancel_submission_hod': {
                'invisible':~Eval('state').in_(
                ['f_to_hod']),
                'depends':['state'],
                },
            'submit_to_ao': {
                'invisible':~Eval('state').in_(
                ['approve_hod']),
                'depends':['state'],
                },
            'approve_for_ao': {
                'invisible':~Eval('state').in_(
                ['f_to_ao']),
                'depends':['state'],
                },
            'cancel_submission_ao': {
                'invisible':~Eval('state').in_(
                ['f_to_ao']),
                'depends':['state'],
                },
            })

    @staticmethod
    def default_state():
        return 'draft'

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

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

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

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

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

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

    @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):
        self.salary_code=self.employee.salary_code if self.employee else ''

    @classmethod
    def default_date(cls):
        present_date = datetime.now()
        return present_date    

    @classmethod
    def default_bill_month(cls):
        month = datetime.today().strftime("%B")
        return month       
Beispiel #3
0
class Sale:
    __name__ = 'sale.sale'

    channel = fields.Many2One(
        'sale.channel', 'Channel', required=True, domain=[
            ('id', 'in', Eval('context', {}).get('allowed_read_channels', [])),
        ],
        states={
            'readonly': Or(
                (Eval('id', default=0) > 0),
                Bool(Eval('lines', default=[])),
            )
        }, depends=['id']
    )

    channel_type = fields.Function(
        fields.Char('Channel Type'), 'on_change_with_channel_type'
    )

    has_channel_exception = fields.Function(
        fields.Boolean('Has Channel Exception ?'), 'get_has_channel_exception'
    )

    def get_has_channel_exception(self, name):
        """
        Returs True if sale has exception
        """
        ChannelException = Pool().get('channel.exception')

        return bool(
            ChannelException.search([
                ('origin', '=', '%s,%s' % (self.__name__, self.id)),
                ('channel', '=', self.channel.id),
                ('is_resolved', '=', False)
            ])
        )

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

        cls._error_messages.update({
            'channel_missing': (
                'Go to user preferences and select a current_channel ("%s")'
            ),
            'channel_change_not_allowed': (
                'Cannot change channel'
            ),
            'not_create_channel': (
                'You cannot create order under this channel because you do not '
                'have required permissions'
            ),
        })

    @classmethod
    def default_channel(cls):
        User = Pool().get('res.user')

        user = User(Transaction().user)
        channel_id = Transaction().context.get('current_channel')

        if channel_id:
            return channel_id
        return user.current_channel and \
            user.current_channel.id  # pragma: nocover

    @staticmethod
    def default_company():
        Sale = Pool().get('sale.sale')
        Channel = Pool().get('sale.channel')

        channel_id = Sale.default_channel()
        if channel_id:
            return Channel(channel_id).company.id

        return Transaction().context.get('company')  # pragma: nocover

    @staticmethod
    def default_invoice_method():
        Sale = Pool().get('sale.sale')
        Channel = Pool().get('sale.channel')
        Config = Pool().get('sale.configuration')

        channel_id = Sale.default_channel()
        if not channel_id:  # pragma: nocover
            config = Config(1)
            return config.sale_invoice_method

        return Channel(channel_id).invoice_method

    @staticmethod
    def default_shipment_method():
        Sale = Pool().get('sale.sale')
        Channel = Pool().get('sale.channel')
        Config = Pool().get('sale.configuration')

        channel_id = Sale.default_channel()
        if not channel_id:  # pragma: nocover
            config = Config(1)
            return config.sale_invoice_method

        return Channel(channel_id).shipment_method

    @staticmethod
    def default_warehouse():
        Sale = Pool().get('sale.sale')
        Channel = Pool().get('sale.channel')
        Location = Pool().get('stock.location')

        channel_id = Sale.default_channel()
        if not channel_id:  # pragma: nocover
            return Location.search([('type', '=', 'warehouse')], limit=1)[0].id
        else:
            return Channel(channel_id).warehouse.id

    @staticmethod
    def default_price_list():
        Sale = Pool().get('sale.sale')
        Channel = Pool().get('sale.channel')

        channel_id = Sale.default_channel()
        if channel_id:
            return Channel(channel_id).price_list.id
        return None  # pragma: nocover

    @staticmethod
    def default_payment_term():
        Sale = Pool().get('sale.sale')
        Channel = Pool().get('sale.channel')

        channel_id = Sale.default_channel()
        if channel_id:
            return Channel(channel_id).payment_term.id
        return None  # pragma: nocover

    @fields.depends('channel', 'party')
    def on_change_channel(self):
        if not self.channel:
            return {}  # pragma: nocover
        res = {}
        for fname in ('company', 'warehouse', 'currency', 'payment_term'):
            fvalue = getattr(self.channel, fname)
            if fvalue:
                res[fname] = fvalue.id
        if (not self.party or not self.party.sale_price_list):
            res['price_list'] = self.channel.price_list.id  # pragma: nocover
        if self.channel.invoice_method:
            res['invoice_method'] = self.channel.invoice_method
        if self.channel.shipment_method:
            res['shipment_method'] = self.channel.shipment_method

        # Update AR record
        for key, value in res.iteritems():
            if '.' not in key:
                setattr(self, key, value)
        return res

    @fields.depends('channel')
    def on_change_party(self):  # pragma: nocover
        res = super(Sale, self).on_change_party()
        channel = self.channel

        if channel:
            if not res.get('price_list') and res.get('invoice_address'):
                res['price_list'] = channel.price_list.id
                res['price_list.rec_name'] = channel.price_list.rec_name
            if not res.get('payment_term') and res.get('invoice_address'):
                res['payment_term'] = channel.payment_term.id
                res['payment_term.rec_name'] = \
                    self.channel.payment_term.rec_name

        # Update AR record
        for key, value in res.iteritems():
            setattr(self, key, value)
        return res

    @fields.depends('channel')
    def on_change_with_channel_type(self, name=None):
        """
        Returns the source of the channel
        """
        if self.channel:
            return self.channel.source

    def check_create_access(self, silent=False):
        """
            Check sale creation in channel
        """
        User = Pool().get('res.user')
        user = User(Transaction().user)

        if user.id == 0:
            return  # pragma: nocover

        if self.channel not in user.allowed_create_channels:
            if silent:
                return False
            self.raise_user_error('not_create_channel')
        return True

    @classmethod
    def write(cls, sales, values, *args):
        """
        Check if channel in sale is is user's create_channel
        """
        if 'channel' in values:
            # Channel cannot be changed at any cost.
            cls.raise_user_error('channel_change_not_allowed')

        super(Sale, cls).write(sales, values, *args)

    @classmethod
    def create(cls, vlist):
        """
        Check if user is allowed to create sale in channel
        """
        User = Pool().get('res.user')
        user = User(Transaction().user)

        for values in vlist:
            if 'channel' not in values and not cls.default_channel():
                cls.raise_user_error(
                    'channel_missing', (user.rec_name,)
                )  # pragma: nocover

        sales = super(Sale, cls).create(vlist)
        for sale in sales:
            sale.check_create_access()
        return sales

    @classmethod
    def copy(cls, sales, default=None):
        """
        Duplicating records
        """
        if default is None:
            default = {}

        for sale in sales:
            if not sale.check_create_access(True):
                default['channel'] = cls.default_channel()

        return super(Sale, cls).copy(sales, default=default)
Beispiel #4
0
class Period(Workflow, ModelSQL, ModelView):
    'Period'
    __name__ = 'tmi.period'
    name = fields.Char('Name', required=True)
    start_date = fields.Date('Starting Date',
                             required=True,
                             states=_STATES,
                             domain=[('start_date', '<=',
                                      Eval('end_date', None))],
                             depends=_DEPENDS + ['end_date'],
                             select=True)
    end_date = fields.Date('Ending Date',
                           required=True,
                           states=_STATES,
                           domain=[('end_date', '>=', Eval('start_date',
                                                           None))],
                           depends=_DEPENDS + ['start_date'],
                           select=True)
    year = fields.Many2One('tmi.year',
                           'Year',
                           required=True,
                           states=_STATES,
                           depends=_DEPENDS,
                           select=True)
    state = fields.Selection([
        ('open', 'Open'),
        ('close', 'Close'),
    ],
                             'State',
                             readonly=True,
                             required=True)
    type = fields.Selection([
        ('standard', 'Standard'),
        ('adjustment', 'Adjustment'),
    ],
                            'Type',
                            required=True,
                            states=_STATES,
                            depends=_DEPENDS,
                            select=True)
    icon = fields.Function(fields.Char("Icon"), 'get_icon')

    @classmethod
    def __setup__(cls):
        super(Period, cls).__setup__()
        cls._order.insert(0, ('start_date', 'ASC'))
        cls._error_messages.update({
            'no_period_date':
            'No period defined for date "%s".',
            'modify_del_period_moves': ('You can not modify/delete '
                                        'period "%s" because it has moves.'),
            'create_period_closed_year':
            ('You can not create '
             'a period on year "%s" because it is closed.'),
            'open_period_closed_year':
            ('You can not open period '
             '"%(period)s" because its year "%(year)s" is '
             'closed.'),
            'close_period_non_posted_move':
            ('You can not close period '
             '"%(period)s" because there are non posted moves '
             '"%(move)s" in this period.'),
            'periods_overlap': ('"%(first)s" and "%(second)s" periods '
                                'overlap.'),
            'check_move_sequence': ('Period "%(first)s" and "%(second)s" '
                                    'have the same sequence.'),
            'year_dates': ('Dates of period "%s" are outside '
                           'are outside it\'s year dates.'),
        })
        cls._transitions |= set((
            ('open', 'close'),
            ('close', 'locked'),
            ('close', 'open'),
        ))
        cls._buttons.update({
            'close': {
                'invisible': Eval('state') != 'open',
                'depends': ['state'],
            },
            'reopen': {
                'invisible': Eval('state') != 'close',
                'depends': ['state'],
            },
            'lock': {
                'invisible': Eval('state') != 'close',
                'depends': ['state'],
            },
        })

    @staticmethod
    def default_state():
        return 'open'

    @staticmethod
    def default_type():
        return 'standard'

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

    @classmethod
    def validate(cls, periods):
        super(Period, cls).validate(periods)
        for period in periods:
            period.check_dates()
            period.check_year_dates()

    @classmethod
    def get_period_ids(cls, name):
        pool = Pool()
        Period = pool.get('tmi.period')
        context = Transaction().context

        period = None
        if name.startswith('start_'):
            period_ids = []
            if context.get('start_period'):
                period = Period(context['start_period'])
        elif name.startswith('end_'):
            period_ids = []
            if context.get('end_period'):
                period = Period(context['end_period'])
            else:
                periods = Period.search([
                    ('year', '=', context.get('year')),
                    ('type', '=', 'standard'),
                ],
                                        order=[('start_date', 'DESC')],
                                        limit=1)
                if periods:
                    period, = periods

        if period:
            periods = Period.search([
                ('year', '=', context.get('year')),
                ('end_date', '<=', period.start_date),
            ])
            if period.start_date == period.end_date:
                periods.append(period)
            if periods:
                period_ids = [p.id for p in periods]
            if name.startswith('end_'):
                # Always include ending period
                period_ids.append(period.id)
        return period_ids

    def check_dates(self):
        if self.type != 'standard':
            return True
        transaction = Transaction()
        connection = transaction.connection
        transaction.database.lock(connection, self._table)
        table = self.__table__()
        cursor = connection.cursor()
        cursor.execute(
            *table.select(table.id,
                          where=(((table.start_date <= self.start_date)
                                  & (table.end_date >= self.start_date))
                                 | ((table.start_date <= self.end_date)
                                    & (table.end_date >= self.end_date))
                                 | ((table.start_date >= self.start_date)
                                    & (table.end_date <= self.end_date)))
                          & (table.year == self.year.id)
                          & (table.type == 'standard')
                          & (table.id != self.id)))
        period_id = cursor.fetchone()
        if period_id:
            overlapping_period = self.__class__(period_id[0])
            self.raise_user_error('periods_overlap', {
                'first': self.rec_name,
                'second': overlapping_period.rec_name,
            })

    def check_year_dates(self):
        if (self.start_date < self.year.start_date
                or self.end_date > self.year.end_date):
            self.raise_user_error('year_dates', (self.rec_name, ))

    @classmethod
    def find(cls, date=None, exception=True, test_state=True):
        '''
        Return the period 
            at the date or the current date.
        If exception is set the function will raise an exception
            if no period is found.
        If test_state is true, it will search on non-closed periods
        '''
        pool = Pool()
        Date = pool.get('ir.date')
        Lang = pool.get('ir.lang')

        if not date:
            date = Date.today()
        clause = [
            ('start_date', '<=', date),
            ('end_date', '>=', date),
            ('type', '=', 'standard'),
        ]
        if test_state:
            clause.append(('state', '=', 'open'))
        periods = cls.search(clause, order=[('start_date', 'DESC')], limit=1)
        if not periods:
            if exception:
                lang = Lang.get()
                cls.raise_user_error('no_period_date', lang.strftime(date))
            else:
                return None
        return periods[0].id

    @classmethod
    def _check(cls, periods):
        Move = Pool().get('tmi.move')
        moves = Move.search([
            ('period', 'in', [p.id for p in periods]),
        ],
                            limit=1)
        if moves:
            cls.raise_user_error('modify_del_period_moves',
                                 (moves[0].period.rec_name, ))

    @classmethod
    def search(cls,
               args,
               offset=0,
               limit=None,
               order=None,
               count=False,
               query=False):
        args = args[:]

        def process_args(args):
            i = 0
            while i < len(args):
                # add test for xmlrpc and pyson that doesn't handle tuple
                if ((isinstance(args[i], tuple) or
                     (isinstance(args[i], list) and len(args[i]) > 2
                      and args[i][1] in OPERATORS))
                        and args[i][0] in ('start_date', 'end_date')
                        and isinstance(args[i][2], (list, tuple))):
                    if not args[i][2][0]:
                        args[i] = ('id', '!=', '0')
                    else:
                        period = cls(args[i][2][0])
                        args[i] = (args[i][0], args[i][1],
                                   getattr(period, args[i][2][1]))
                elif isinstance(args[i], list):
                    process_args(args[i])
                i += 1

        process_args(args)
        return super(Period, cls).search(args,
                                         offset=offset,
                                         limit=limit,
                                         order=order,
                                         count=count,
                                         query=query)

    @classmethod
    def create(cls, vlist):
        FiscalYear = Pool().get('tmi.year')
        vlist = [x.copy() for x in vlist]
        for vals in vlist:
            if vals.get('year'):
                year = FiscalYear(vals['year'])
                if year.state != 'open':
                    cls.raise_user_error('create_period_closed_year',
                                         (year.rec_name, ))
        return super(Period, cls).create(vlist)

    @classmethod
    def write(cls, *args):
        Move = Pool().get('tmi.move')
        actions = iter(args)
        args = []
        for periods, values in zip(actions, actions):
            for key, value in values.items():
                if key in ('start_date', 'end_date', 'year'):

                    def modified(period):
                        if key in ['start_date', 'end_date']:
                            return getattr(period, key) != value
                        else:
                            return period.year.id != value

                    cls._check(list(filter(modified, periods)))
                    break
            if values.get('state') == 'open':
                for period in periods:
                    if period.year.state != 'open':
                        cls.raise_user_error(
                            'open_period_closed_year', {
                                'period': period.rec_name,
                                'year': period.year.rec_name,
                            })
            args.extend((periods, values))
        super(Period, cls).write(*args)

    @classmethod
    def delete(cls, periods):
        cls._check(periods)
        super(Period, cls).delete(periods)

    @classmethod
    @ModelView.button
    @Workflow.transition('close')
    def close(cls, periods):

        transaction = Transaction()
        database = transaction.database
        connection = transaction.connection
        Period = Pool().get('tmi.period')

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

        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('open')
    def reopen(cls, periods):
        "Re-open period"
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('locked')
    def lock(cls, periods):
        pass
Beispiel #5
0
class Line(ModelSQL, ModelView):
    'Analytic Line'
    __name__ = 'analytic_account.line'
    name = fields.Char('Name', required=True)
    debit = fields.Numeric('Debit',
                           digits=(16, Eval('currency_digits', 2)),
                           required=True,
                           depends=['currency_digits'])
    credit = fields.Numeric('Credit',
                            digits=(16, Eval('currency_digits', 2)),
                            required=True,
                            depends=['currency_digits'])
    currency = fields.Function(
        fields.Many2One('currency.currency', 'Currency'),
        'on_change_with_currency')
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
                                      'on_change_with_currency_digits')
    company = fields.Function(fields.Many2One('company.company', 'Company'),
                              'on_change_with_company')
    account = fields.Many2One('analytic_account.account',
                              'Account',
                              required=True,
                              select=True,
                              domain=[
                                  ('type', '!=', 'view'),
                                  [
                                      'OR',
                                      ('company', '=', None),
                                      ('company', '=', Eval('company', -1)),
                                  ],
                              ],
                              depends=['company'])
    move_line = fields.Many2One('account.move.line',
                                'Account Move Line',
                                ondelete='CASCADE',
                                required=True)
    journal = fields.Many2One('account.journal',
                              'Journal',
                              required=True,
                              select=True)
    date = fields.Date('Date', required=True)
    reference = fields.Char('Reference')
    party = fields.Many2One('party.party', 'Party')
    active = fields.Boolean('Active', select=True)

    @classmethod
    def __setup__(cls):
        super(Line, cls).__setup__()
        t = cls.__table__()
        cls._sql_constraints += [
            ('credit_debit',
             Check(t, (t.credit * t.debit == 0) & (t.credit + t.debit >= 0)),
             'Wrong credit/debit values.'),
        ]
        cls._error_messages.update({
            'line_on_view_account': ('You can not create a move line using '
                                     'view account "%s".'),
            'line_on_inactive_account': ('You can not create a move line '
                                         'using inactive account "%s".'),
        })
        cls._order.insert(0, ('date', 'ASC'))

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

        # Migration from 1.2 currency has been changed in function field
        table.not_null_action('currency', action='remove')

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

    @staticmethod
    def default_active():
        return True

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

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

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

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

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

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

    @classmethod
    def validate(cls, lines):
        super(Line, cls).validate(lines)
        for line in lines:
            line.check_account()

    def check_account(self):
        if self.account.type == 'view':
            self.raise_user_error('line_on_view_account',
                                  (self.account.rec_name, ))
        if not self.account.active:
            self.raise_user_error('line_on_inactive_account',
                                  (self.account.rec_name, ))
Beispiel #6
0
class SaleSecondaryMixin:
    __slots__ = ()
    sale_secondary_uom = fields.Many2One('product.uom',
                                         "Sale Secondary UOM",
                                         domain=[
                                             ('category', '!=',
                                              Eval('default_uom_category')),
                                         ],
                                         depends=['default_uom_category'])
    sale_secondary_uom_factor = fields.Float(
        "Sale Secondary UOM Factor",
        digits=uom_conversion_digits,
        states={
            'required': Bool(Eval('sale_secondary_uom')),
            'invisible': ~Eval('sale_secondary_uom'),
        },
        depends=['sale_secondary_uom'],
        help="The coefficient for the formula:\n"
        "coefficient (sale unit) = 1 (secondary unit)")
    sale_secondary_uom_rate = fields.Float(
        "Sale Secondary UOM Rate",
        digits=uom_conversion_digits,
        states={
            'required': Bool(Eval('sale_secondary_uom')),
            'invisible': ~Eval('sale_secondary_uom'),
        },
        depends=['sale_secondary_uom'],
        help="The coefficient for the formula:\n"
        "1 (sale unit) = coefficient (secondary unit)")

    @fields.depends('sale_secondary_uom_factor')
    def on_change_sale_secondary_uom_factor(self):
        if not self.sale_secondary_uom_factor:
            self.sale_secondary_uom_rate = None
        else:
            self.sale_secondary_uom_rate = round(
                1. / self.sale_secondary_uom_factor, uom_conversion_digits[1])

    @fields.depends('sale_secondary_uom_rate')
    def on_change_sale_secondary_uom_rate(self):
        if not self.sale_secondary_uom_rate:
            self.sale_secondary_uom_factor = None
        else:
            self.sale_secondary_uom_factor = round(
                1. / self.sale_secondary_uom_rate, uom_conversion_digits[1])

    @property
    def sale_secondary_uom_normal_rate(self):
        uom = self.sale_secondary_uom
        rate = self.sale_secondary_uom_rate
        if self.sale_uom and rate and uom:
            if self.sale_uom.accurate_field == 'factor':
                rate *= self.sale_uom.factor
            else:
                rate /= self.sale_uom.rate
            if uom.accurate_field == 'factor':
                rate /= uom.factor
            else:
                rate *= uom.rate
        return rate

    @property
    def sale_secondary_uom_normal_factor(self):
        uom = self.sale_secondary_uom
        factor = self.sale_secondary_uom_factor
        if uom and factor and self.sale_uom:
            if uom.accurate_field == 'factor':
                factor *= uom.factor
            else:
                factor /= uom.rate
            if self.sale_uom.accurate_field == 'factor':
                factor /= self.sale_uom.factor
            else:
                factor *= self.sale_uom.rate
        return factor

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

    def check_sale_secondary_uom_factor_and_rate(self):
        factor = self.sale_secondary_uom_factor
        rate = self.sale_secondary_uom_rate
        if factor and rate:
            if ((rate != round(1. / factor, uom_conversion_digits[1]))
                    and factor != round(1. / rate, uom_conversion_digits[1])):
                raise UOMValidationError(
                    gettext(
                        'sale_secondary_unit'
                        '.msg_sale_secondary_uom_incompatible_factor_rate',
                        record=self))
Beispiel #7
0
from dateutil.relativedelta import relativedelta
from trytond.model import ModelView, ModelSQL, Workflow, fields
from trytond.wizard import Wizard, StateView, StateAction, Button
from trytond.tools import datetime_strftime
from trytond.pyson import Eval, If, PYSONEncoder
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.const import OPERATORS

__all__ = [
    'Year',
    'Period',
]

_STATES = STATES = {
    'readonly': Eval('state') != 'open',
}
_DEPENDS = DEPENDS = ['state']


class Year(Workflow, ModelSQL, ModelView):
    'Tmi Year'
    __name__ = 'tmi.year'
    name = fields.Char('Name', size=None, required=True, depends=DEPENDS)
    start_date = fields.Date('Starting Date',
                             required=True,
                             states=STATES,
                             domain=[('start_date', '<=',
                                      Eval('end_date', None))],
                             depends=DEPENDS + ['end_date'])
    end_date = fields.Date('Ending Date',
class StockMoveSerialNumber(ModelSQL, ModelView):
  'Stock Move Serial Number'
  __name__ = 'stock.move.serial_number'

  input_move = fields.Many2One('stock.move', 'Input Stock Move',
                               ondelete='CASCADE', select=True, required=True,
                               states={
                                 'readonly' : Eval('state') != 'draft',
                               })
  output_move = fields.Many2One('stock.move', 'Output Stock Move',
                                ondelete='SET NULL', select=True,
                                states={
                                  'readonly' : Eval('state') == 'completed',
                                })
  state = fields.Selection([('draft', 'Draft'),
                            ('stored', 'Stored'),
                            ('completed', 'Completed')], 
                           'State', readonly=True, required=True)
  serial_number = fields.Char('Serial Number', required=True)
  sequence = fields.Integer('Sequence')
  
  product = fields.Function(fields.Many2One('product.product', 'Product', readonly=True),
                            'get_product', searcher="search_product")
  input_date = fields.Function(fields.Date("Input Date", readonly=True),
                               'get_input_date', searcher="search_input_date")
  output_date = fields.Function(fields.Date("Output Date", readonly=True),
                               'get_output_date', searcher="search_output_date")

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

    cls._error_messages.update({
      'already_stored' : 'Serial number "%s" already stored',
      'duplicate_serial_numbers' : '"%s", Duplicate Serial Number'
    })

  def get_rec_name(self, name=None):
    return ("%s - %s" % (self.serial_number, self.product.rec_name))

  def get_product(self, name=None):
    return self.input_move.product.id

  def get_input_date(self, name=None):
    return self.input_move.effective_date

  def get_output_date(self, name=None):
    return (self.output_move.effective_date if self.output_move else None)

  @classmethod
  def search_product(cls, name, clause):
    field, operator, value = clause

    if (field == 'product') and (operator == 'ilike'):
      return ['OR',
                ('input_move.product.code', operator, value),
                ('input_move.product.template.name', operator, value)]
    else:
      return [(field.replace('product', 'input_move.product'), operator, value)]

  @classmethod
  def search_input_date(cls, name, clause):
    return [('input_move.effective_date',) + tuple(clause[1:])]

  @classmethod
  def search_output_date(cls, name, clause):
    return [('output_move.effective_date',) + tuple(clause[1:])]

  @classmethod
  def search_rec_name(cls, name, clause):
    _, operator, value = clause

    return ['OR',
             ('input_move.product.code', operator, value),
             ('input_move.product.template.name', operator, value),
             ('serial_number', operator, value)]

  @classmethod
  def default_state(cls):
    return 'draft'

  def __get_state(self):
    state = 'draft'

    if self.input_move.state == 'done':
      state = 'stored'

      if self.output_move and (self.output_move.state == 'done'):
        state = 'completed'
    return state

  def set_state(self):
    state = self.__get_state()
    if self.state != state:
      self.write([self], {'state' : state})

  @classmethod
  def is_valid_serial_number(cls, stock_move, serial_number):
    if type(stock_move) == int:
      stock_move = Pool().get('stock.move')(stock_move)

    result = cls.search([('serial_number', '=', serial_number),
                         ('product', '=', stock_move.product),
                         ('state', 'in', ('draft', 'stored'))])
    return (len(result) == 0)

  @classmethod
  def create(cls, vlist):
    serial_numbers = []
    for record in vlist:
      serial_number = record['serial_number']

      if serial_number in serial_numbers:
        cls.raise_user_error('duplicate_serial_numbers', serial_number)
      else:
        serial_numbers.append(serial_number)

      if not cls.is_valid_serial_number(record['input_move'], serial_number):
        cls.raise_user_error('already_stored', serial_number)
    
    return super(StockMoveSerialNumber, cls).create(vlist) or vlist

  @classmethod
  def write(cls, *args):
    serial_numbers = []

    current_record = args[0]
    for argument in args:
      if type(argument) == dict:
        if 'serial_number' in argument:
          serial_number = argument['serial_number']
          
          if serial_number in serial_numbers:
            cls.raise_user_error('duplicate_serial_numbers', serial_number)
          else:
            serial_numbers.append(serial_number)
          
          if not cls.is_valid_serial_number(current_record.input_move, serial_number):
            cls.raise_user_error('already_stored', serial_number)
      else:
        current_record = argument[0]
    super(StockMoveSerialNumber, cls).write(*args)
class StockMove:
  __name__ = 'stock.move'

  input_serial_numbers = fields.One2Many('stock.move.serial_number',
                                         'input_move', 'Input Serial Numbers',
                                         states={
                                           'readonly': Or(
                                                         In(Eval('state'), ['cancel', 'assigned', 'done']),
                                                         Not(Bool(Eval('product')))
                                                       ),
                                           'invisible' : Not(Bool(Eval('input_type_stock_move')))
                                         },
                                         depends=['input_type_stock_move', 'state'])
  output_serial_numbers = fields.One2Many('stock.move.serial_number',
                                          'output_move', 'Output Serial Numbers',
                                          states={
                                            'readonly': Or(
                                                          In(Eval('state'), ['cancel', 'assigned', 'done']),
                                                          Not(Bool(Eval('product')))
                                                        ),
                                            'invisible' : Not(Bool(Eval('output_type_stock_move')))
                                          },
                                          depends=['output_type_stock_move', 'state'],
                                          add_remove=[('state', '=', 'stored'),
                                                      ('output_move', '=', None),
                                                      ('input_move.product', '=', Eval('product'))])
  input_type_stock_move = fields.Function(fields.Boolean('Input Type Stock Move'),
                                          'on_change_with_input_type_stock_move')
  output_type_stock_move = fields.Function(fields.Boolean('Output Type Stock Move'),
                                           'on_change_with_output_type_stock_move')

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

    cls._error_messages.update({
      'invalid_serial_number' : 'Serial number "%s" already stored'
    })

  @fields.depends('from_location', 'shipment')
  def on_change_with_input_type_stock_move(self, name=None):
    if self.from_location:
      location_type = self.from_location.type
      if location_type == 'production':
        return True

      if self.shipment:
        shipment_type = self.shipment.__name__
        if (shipment_type == 'stock.shipment.in') and (location_type == 'supplier'):
          return True
        if (shipment_type == 'stock.shipment.out.return') and (location_type == 'customer'):
          return True
    return False

  @fields.depends('to_location', 'shipment')
  def on_change_with_output_type_stock_move(self, name=None):
    if self.to_location and self.shipment:
      location_type = self.to_location.type
      shipment_type = self.shipment.__name__

      if (shipment_type == 'stock.shipment.out') and (location_type == 'customer'):
        return True
      if (shipment_type == 'stock.shipment.in.return') and (location_type == 'supplier'):
        return True
    return False

  @fields.depends('product', 'input_serial_numbers')
  def on_change_product(self):
    changes = super(StockMove, self).on_change_product()
    
    changes['output_serial_numbers'] = []
    if self.product:
      for record in self.input_serial_numbers:
        if not StockMoveSerialNumber.is_valid_serial_number(self, record.serial_number):
          self.raise_user_error('invalid_serial_number', record.serial_number)
    return changes

  @classmethod
  def search_rec_name(cls, name, clause):
    try:
      _, operator, value = clause

      return [('id', operator, int(value))]
    except ValueError:
      return super(StockMove, cls).search_rec_name(name, clause)

  @classmethod
  def do(cls, moves):
    super(StockMove, cls).do(moves)
    
    for move in moves:
      for serial_number in move.input_serial_numbers:
        serial_number.set_state()

      for serial_number in move.output_serial_numbers:
        serial_number.set_state()
Beispiel #10
0
 def __setup__(cls):
     super().__setup__()
     cls.producible.states = {
         'invisible': ~Eval('type').in_(cls.get_producible_types()),
         }
Beispiel #11
0
 def view_attributes(cls):
     return super().view_attributes() + [
         ('//page[@id="production"]', 'states', {
                 'invisible': ~Eval('producible'),
                 })]
Beispiel #12
0
    def test_on_create(self):
        'Test on_create'
        pool = Pool()
        Model = pool.get('ir.model')
        Trigger = pool.get('ir.trigger')
        Triggered = pool.get('test.triggered')

        model, = Model.search([
            ('model', '=', 'test.triggered'),
        ])

        trigger, = Trigger.create([{
            'name': 'Test',
            'model': model.id,
            'on_create': True,
            'condition': 'true',
            'action': 'test.trigger_action|trigger',
        }])

        triggered, = Triggered.create([{
            'name': 'Test',
        }])

        self.run_tasks()
        self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
        TRIGGER_LOGS.pop()

        # Trigger with condition
        condition = PYSONEncoder().encode(
            Eval('self', {}).get('name') == 'Bar')
        Trigger.write([trigger], {
            'condition': condition,
        })

        # Matching condition
        triggered, = Triggered.create([{
            'name': 'Bar',
        }])
        self.run_tasks()
        self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
        TRIGGER_LOGS.pop()

        # Non matching condition
        triggered, = Triggered.create([{
            'name': 'Foo',
        }])
        self.run_tasks()
        self.assertEqual(TRIGGER_LOGS, [])

        # With limit number
        Trigger.write([trigger], {
            'condition': 'true',
            'limit_number': 1,
        })
        triggered, = Triggered.create([{
            'name': 'Test',
        }])
        self.run_tasks()
        self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
        TRIGGER_LOGS.pop()

        # With minimum delay
        Trigger.write([trigger], {
            'limit_number': 0,
            'minimum_time_delay': datetime.timedelta(hours=1),
        })
        triggered, = Triggered.create([{
            'name': 'Test',
        }])
        self.run_tasks()
        self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
        TRIGGER_LOGS.pop()

        # Restart the cache on the get_triggers method of ir.trigger
        Trigger._get_triggers_cache.clear()
Beispiel #13
0
    def test_on_time(self):
        'Test on_time'
        pool = Pool()
        Model = pool.get('ir.model')
        Trigger = pool.get('ir.trigger')
        Triggered = pool.get('test.triggered')
        TriggerLog = pool.get('ir.trigger.log')

        model, = Model.search([
            ('model', '=', 'test.triggered'),
        ])

        trigger, = Trigger.create([{
            'name': 'Test',
            'model': model.id,
            'on_time': True,
            'condition': 'true',
            'action': 'test.trigger_action|trigger',
        }])

        triggered, = Triggered.create([{
            'name': 'Test',
        }])
        Trigger.trigger_time()
        self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
        TRIGGER_LOGS.pop()

        # Trigger with condition
        condition = PYSONEncoder().encode(
            Eval('self', {}).get('name') == 'Bar')
        Trigger.write([trigger], {
            'condition': condition,
        })

        # Matching condition
        Triggered.write([triggered], {
            'name': 'Bar',
        })
        Trigger.trigger_time()
        self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
        TRIGGER_LOGS.pop()

        # Non matching condition
        Triggered.write([triggered], {
            'name': 'Foo',
        })
        Trigger.trigger_time()
        self.assertEqual(TRIGGER_LOGS, [])

        # With limit number
        Trigger.write([trigger], {
            'condition': 'true',
            'limit_number': 1,
        })
        Trigger.trigger_time()
        Trigger.trigger_time()
        self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
        TRIGGER_LOGS.pop()

        # Delete trigger logs of limit number test
        TriggerLog.delete(TriggerLog.search([
            ('trigger', '=', trigger.id),
        ]))

        # With minimum delay
        Trigger.write([trigger], {
            'limit_number': 0,
            'minimum_time_delay': datetime.timedelta.max,
        })
        Trigger.trigger_time()
        Trigger.trigger_time()
        self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
        TRIGGER_LOGS.pop()
        Transaction().delete = {}

        # Delete trigger logs of previous minimum delay test
        TriggerLog.delete(TriggerLog.search([
            ('trigger', '=', trigger.id),
        ]))

        Trigger.write([trigger], {
            'minimum_time_delay': datetime.timedelta(seconds=1),
        })
        Trigger.trigger_time()

        # Make time pass by moving back in time the log creation
        trigger_log = TriggerLog.__table__()
        cursor = Transaction().connection.cursor()
        cursor.execute(*trigger_log.update(
            [trigger_log.create_date],
            [datetime.datetime.now() - datetime.timedelta(days=1)],
            where=trigger_log.record_id == triggered.id))

        Trigger.trigger_time()
        self.assertEqual(TRIGGER_LOGS, [([triggered], trigger),
                                        ([triggered], trigger)])
        TRIGGER_LOGS.pop()
        TRIGGER_LOGS.pop()
        Transaction().delete = {}

        # Restart the cache on the get_triggers method of ir.trigger
        Trigger._get_triggers_cache.clear()
class PaymentTransaction:
    """
    Implement the authorize and capture methods
    """
    __name__ = 'payment_gateway.transaction'

    gift_card = fields.Many2One('gift_card.gift_card',
                                'Gift Card',
                                domain=[('state', '=', 'active')],
                                states={
                                    'required': Eval('method') == 'gift_card',
                                    'readonly': Eval('state') != 'draft'
                                },
                                select=True)

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

        cls._error_messages.update({
            'insufficient_amount':
            'Card %s is found to have insufficient amount',
            'negative_amount':
            'The amount to be entered cannot be negative.',
        })

        cls._buttons['authorize']['invisible'] = \
            cls._buttons['authorize']['invisible'] & ~(
                (Eval('state') == 'draft') &
                (Eval('method') == 'gift_card')
        )
        cls._buttons['capture']['invisible'] = \
            cls._buttons['capture']['invisible'] & ~(
                (Eval('state') == 'draft') &
                (Eval('method') == 'gift_card')
        )

        cls._buttons['settle']['invisible'] = \
            cls._buttons['settle']['invisible'] & ~(
                (Eval('state') == 'authorized') &
                (Eval('method') == 'gift_card')
        )

    def validate_gift_card_amount(self, available_amount):
        """
        Validates that gift card has sufficient amount to pay
        """
        if self.amount < 0:
            # TODO:
            # Put this bit in payment_gateway.
            self.raise_user_error("negative_amount")
        if available_amount < self.amount:
            self.raise_user_error("insufficient_amount", self.gift_card.number)

    def authorize_self(self):
        """
        Authorize using gift card for the specific transaction.
        """
        if self.method == 'gift_card':
            self.validate_gift_card_amount(self.gift_card.amount_available)

        return super(PaymentTransaction, self).authorize_self()

    def capture_self(self):
        """
        Capture using gift card for the specific transaction.
        """
        if self.method == 'gift_card':
            self.validate_gift_card_amount(self.gift_card.amount_available)

        return super(PaymentTransaction, self).capture_self()

    def settle_self(self):
        """
        Settle using gift card for the specific transaction.
        """
        if self.method == 'gift_card':
            # Ignore authorized amount as settlement will be done for
            # previously authorized amount
            available_amount = \
                self.gift_card.amount - self.gift_card.amount_captured
            self.validate_gift_card_amount(available_amount)

        return super(PaymentTransaction, self).settle_self()

    @classmethod
    @ModelView.button
    @Workflow.transition('posted')
    def post(cls, transactions):
        """
        Complete the transactions by creating account moves and post them.

        This method is likely to end in failure if the initial configuration
        of the journal and fiscal periods have not been done. You could
        alternatively use the safe_post instance method to try to post the
        record, but ignore the error silently.
        """
        rv = super(PaymentTransaction, cls).post(transactions)

        for transaction in transactions:
            if transaction.gift_card and \
                    transaction.gift_card.amount_available == 0:
                transaction.gift_card.state = 'used'
                transaction.gift_card.save()
        return rv
Beispiel #15
0
class HrSalaryRule(ModelSQL, ModelView):
    'Salary Rule'

    __name__ = 'hr.salary.rule'

    name = fields.Char('Name', required=True)
    code = fields.Char('Code', required=True)
    active = fields.Boolean('Active')
    category = fields.Many2One('hr.salary.rule.category', 'Category')
    condition_select = fields.Selection([
        ('always_true', 'Always True'),
        ('range', 'Range'),
        ('python_code', 'Python Code'),
    ],
                                        'Condition Type',
                                        required=True)
    condition_range_max = fields.Float(
        'Maximum Range',
        states={'invisible': ~Eval('condition_select').in_(['range'])},
        depends=['condition_select'])
    condition_range_min = fields.Float(
        'Minimum Range',
        states={'invisible': ~Eval('condition_select').in_(['range'])},
        depends=['condition_select'])
    amount_select = fields.Selection([('percentage', 'Percentage'),
                                      ('fix', 'Fix'), ('code', 'Code')],
                                     'Amount Type',
                                     required=True)
    amount_fix = fields.Float(
        'Fix Amount',
        states={'invisible': ~Eval('amount_select').in_(['fix'])},
        depends=['amount_select'])
    amount_percentage = fields.Float(
        'Percentage(%)',
        states={'invisible': ~Eval('amount_select').in_(['percentage'])},
        depends=['amount_select'])
    percentage_based = fields.Many2One(
        'hr.salary.rule',
        'Percentage Based on',
        states={'invisible': ~Eval('amount_select').in_(['percentage'])},
        depends=['amount_select'])
    note = fields.Text('Description')
    priority = fields.Integer('Priority', required=True)

    @staticmethod
    def default_active():
        return True

    @classmethod
    def validate(cls, records):
        super(HrSalaryRule, cls).validate(records)
        for record in records:
            if record.amount_select == 'amount_percentage':
                if record.amount_percentage not in range(0, 101):
                    cls.raise_user_error('Invalid Amount')

    @staticmethod
    def default_condition_select():
        return 'always_true'

    def check(self, payslip, employee, contract):
        if self.condition_select == 'always_true':
            return True
        if self.condition_select == 'range':
            pass
            #TODO: Write the code for range here
            return True
        if self.condition_select == 'python_code':
            method_name = "check_" + str(self.code)
            if hasattr(self, method_name):
                method = getattr(self, method_name)
                res = method(payslip, employee, contract)
                return res
            else:
                #TODO: Raise error that the rule has python_code as condition but there is no method
                return True

    def calculate(self, payslip, employee, contract):
        method_name = "calculate_" + str(self.code)
        if hasattr(self, method_name):
            method = getattr(self, method_name)
            print(method, "methodmethod")
            res = method(payslip, employee, contract)
            print(res, "RESESE")
            return res

    def calculate_GROSS(self, payslip, employee, contract):
        amount = 0
        payslip_line = Pool().get('hr.payslip.line')
        payslip_lines = payslip_line.search([('category.code', 'in', ('BASIC',
                                                                      'ALW'))])
        if payslip_lines:
            for line in payslip_lines:
                if line.payslip == payslip:
                    amount += line.amount
        return amount

    def calculate_BASIC(self, payslip, employee, contract):
        return self.calculate_NEW_BASIC(payslip, employee, contract)

    def calculate_NET(self, payslip, employee, categories):
        amount = 0
        '''
        Takes Parameters - payslip, employee and #categories

        returns the value of Non Practicing Allowance to be added
        '''
        # TODO: categories to be corrected
        gross = self.calculate_GROSS(payslip, employee, categories)
        payslip_line = Pool().get('hr.payslip.line')
        payslip_lines = payslip_line.search([('category.code', '=', 'DED')])
        if payslip_lines:
            for line in payslip_lines:
                if line.payslip == payslip:
                    amount += line.amount
        return gross - amount

    def calculate_NPA(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Non Practicing Allowance to be added
        '''
        if employee.designation.name in (
                "Scientist I",
                "Scientist I (Absorption)",
                "Scientist II",
                "Scientist II (Absorption)",
                "Scientist III",
                "Scientist III (Absorption)",
                "Scientist IV",
                "Scientist IV (Absorption)",
                "Scientist V (Absorption)",
                "Assistant Professor",
                "Associate Professor",
                "professor",
                "Lecturer",
                "Principal",
                "Director",
        ):
            # TODO: Check for faculty designations, might
            # coincide with nursing faculty. Requires only MBBS
            npa = (0.2 * contract.basic)
            return npa
        else:
            npa = 0
            return npa

    def calculate_NEW_BASIC(self, payslip, employee, contract):
        npa = self.calculate_NPA(payslip, employee, contract)
        if npa:
            # TODO: change the rules for new_basic if any
            new_basic = npa + contract.basic
            if new_basic > 23750:
                new_basic = 23750
            return new_basic
        else:
            new_basic = contract.basic
            return new_basic

    def calculate_DA(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Dearness Allowance to be added
        '''
        if self.calculate_NEW_BASIC(payslip, employee, contract):
            # TODO: change the rules for new_basic
            dear_alw = (0.12 *
                        self.calculate_NEW_BASIC(payslip, employee, contract))
            return dear_alw
        else:
            dear_alw = (0.12 * contract.basic)
            return dear_alw

    def calculate_NURSING_ALW(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Nursing Allowance to be added
        '''
        if employee.designation.name in (
                "Assistant Nursing Superintendent",
                "Chief Nursing Officer",
                "Deputy Nursing Superintendent",
                "Nursing Officer",
                "Nursing Superintendent",
                "Sr. Nursing Officer",
                "Public Health Nurse",
                "Public Health Nurse (Supervisor)",
                "Tutor in Nursing",
                "Senior Nursing Tutor",
                "Senior Nursing Officer",
        ):
            nurse_alw = 7200
            return nurse_alw
        return None

    def calculate_TRANSPORT_ALW(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Transport Allowance to be added
        '''
        # TODO: Write the condition if the employee is handicapped
        # if employee.handicap_status == True:
        trans_alw = 0
        if int(employee.grade_pay.name) >= 5400:
            trans_alw = 7200 + self.calculate_DA(payslip, employee, contract)
        elif int(employee.grade_pay.name) < 5400:
            trans_alw = 3600 + self.calculate_DA(payslip, employee, contract)
        elif (employee.pay_in_band) > 17:
            trans_alw = 14400 + self.calculate_DA(payslip, employee, contract)
        elif (employee.designation.name) == 'Centre Chief':
            trans_alw = 15750 + self.calculate_DA(payslip, employee, contract)
        if employee.category == "ph":
            trans = trans_alw * 2
            trans_alw = trans
        else:
            trans_alw = trans_alw
        return trans_alw

    def calculate_HPCA(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Hospital Patient Care Allowance to be added
        '''
        if employee.group == "C" or employee.group == "D":
            if employee.grade_pay >= 1800 and  \
               employee.grade_pay <= 2800:
                hpca = 4100
                return hpca
        return None

    def calculate_ACAD_ALW(self, payslip, employee, contract):
        # TODO: Check for Faculty designations
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Academic Allowance to be added
        '''
        if employee.designation.name in (
                "Assistant Professor",
                "Associate Professor",
                "Professor",
                "Lecturer",
                "Principal",
                "Director",
        ):
            acad_alw = 22500
            return acad_alw
        return None

    def calculate_UNIFORM_ALW(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Uniform Allowance to be added
        '''
        if employee.designation.name in (
                "Assistant Nursing Superintendent",
                "Chief Nursing Officer",
                "Deputy Nursing Superintendent",
                "Nursing Officer",
                "Sr. Nursing Officer",
                "Nursing Superintendent",
                "Public Health Nurse",
                "Public Health Nurse (Supervisor)",
                "Tutor in Nursing",
                "Senior Nursing Tutor",
                "Senior Nursing Officer",
        ):
            uni_alw = 1800
            return uni_alw
        return None

    def calculate_DEP_ALW(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Deputation Allowance to be added
        '''
        pass

    def calculate_TOOL_ALW(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Tool Allowance to be added
        '''
        if employee.group == "D" and employee.department == "Engineering Services Division (ESD)":
            if employee.designation.name in (
                    "Plumber",
                    "Meson",
                    "Electrician",
                    "Wireman",
                    "Foreman",
                    "Mechanic",
            ):
                tool_alw = 10
                return tool_alw
            elif employee.designation.name == "Carpenter":
                tool_alw = 15
                return tool_alw
            elif employee.designation.name == "Mali":
                tool_alw = 180
                return tool_alw
        return None

    def calculate_OT(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Overtime Allowance to be added
        '''
        pass

    def calculate_QPAY(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Qualification Pay Allowance to be added
        '''
        pass

    def calculate_DRESS_ALW(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Dress Allowance to be added
        '''
        if datetime.date.today().month == 7:
            if employee.designation.name in (
                    "Hospital Attendant Grade I",
                    "Hospital Attendant Grade II",
                    "Hospital Attendant Grade III",
                    "Sanitary Attendant Grade I",
                    "Sanitary Attendant Grade II",
                    "Sanitary Attendant Grade III",
            ):
                dress_alw = 5000
                return dress_alw
        else:
            dress_alw = 0
            return dress_alw

    def calculate_ICU_ALW(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of ICU Allowance to be added
        '''
        pass

    def calculate_CHTA(self, payslip, employee, contract):
        pass

    def calculate_EHS(self, payslip, employee, contract):
        '''
        Takes parameters - payslip, employee and contract

        returns the value of EHS to be deducted
        '''
        if int(employee.grade_pay.name) >= 7600:
            ehs = 1000
            return ehs
        elif int(employee.grade_pay.name) >= 4600 and \
            int(employee.grade_pay.name) <= 6600:
            ehs = 650
            return ehs
        elif int(employee.grade_pay.name) == 4200:
            ehs = 450
            return ehs
        elif int(employee.grade_pay.name) < 4200:
            ehs = 250
            return ehs

    def calculate_KU(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of KARMCHARI UNION to be deducted
        '''
        if employee.employee_group in ("C", "D"):
            karmchari_ded = 10
            return karmchari_ded
        return None

    def calculate_OA(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of OFFICER ASSOCIATION to be deducted
        '''
        if employee.employee_group in ("A", "B"):
            officer_ded = 20
            return officer_ded
        return None

    def calculate_NU(self, payslip, employee, contract):
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of NURSING UNION to be deducted
        '''
        if employee.designation.name in (
                "Assistant Nursing Superintendent",
                "Chief Nursing Officer",
                "Deputy Nursing Superintendent",
                "Nursing Officer",
                "Nursing Superintendent",
                "Sr. Nursing Officer",
                "Public Health Nurse",
                "Public Health Nurse (Supervisor)",
                "Tutor in Nursing",
                "Senior Nursing Tutor",
                "Senior Nursing Officer",
        ):
            nurse_ded = 20
            return nurse_ded
        return None

    def calculate_FAC_FUND_CLUB(self, payslip, employee, contract):
        # TODO : In which month Faculty Fund will be deducted
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of Faculity Fund to be deducted
        '''
        if employee.employee_group in ("A", "B", "C", "D"):
            faculity_fund = 500
            return faculity_fund
        return None

    def calculate_EIS_DED(self, payslip, employee, contract):
        # TODO : In which year EIS deduction stopped
        '''
        Takes Parameters - payslip, employee and contract

        returns the value of EIS to be deducted
        '''
        if employee.employee_group == 'A':
            eis = 100
            return eis
        elif employee.employee_group == 'B':
            eis = 60
            return eis
        elif employee.employee_group == 'C':
            eis = 30
            return eis
        elif employee.employee_group == 'D':
            eis = 20
            return eis

    def check_BASIC(self, payslip, employee, contract):
        return contract.basic

    def check_NET(self, payslip, employee, categories):
        #categories.name.BASIC + categories.ALW + categories.DED
        return True

    def check_NPA(self, payslip, employee, contract):
        if employee.employee_group == 'A':
            npa = (0.2 * contract.basic)
            return contract.basic + npa

    def check_NEW_BASIC(self, payslip, employee, contract):
        pass

    def check_UNIFORM_ALW(self, payslip, employee, contract):
        pass

    def check_DA(self, payslip, employee, contract):
        pass

    def check_NURSE_ALW(self, payslip, employee, contract):
        pass

    def check_TRANSPORT_ALW(self, payslip, employee, contract):
        pass

    def check_HPCA(self, payslip, employee, contract):
        pass

    def check_ACAD_ALW(self, payslip, employee, contract):
        pass

    def check_DEP_ALW(self, payslip, employee, contract):
        pass

    def check_TOOL_ALW(self, payslip, employee, contract):
        pass

    def check_QPAY(self, payslip, employee, contract):
        pass

    def check_DRESS_ALW(self, payslip, employee, contract):
        pass

    def check_CHTA(self, payslip, employee, contract):
        pass

    def check_EHS(self, payslip, employee, contract):
        pass

    def check_ASSN_FEE(self, payslip, employee, contract):
        pass

    def check_KARAMCHARI(self, payslip, employee, contract):
        pass

    def check_NURSE_UNION(self, payslip, employee, contract):
        pass

    def check_OF_ASSN(self, payslip, employee, contract):
        pass

    def check_FAC_CLUB(self, payslip, employee, contract):
        pass

    def check_RDA(self, payslip, employee, contract):
        pass

    def check_LEAVES(self, payslip, employee, contract):
        pass

    def check_COURT_RECOVERY(self, payslip, employee, contract):
        pass
Beispiel #16
0
class Product(metaclass=PoolMeta):
    __name__ = 'product.product'
    # TODO: Phantom should only be available with products whose UOM is 'unit'
    phantom = fields.Boolean('Phantom', states={
            'invisible': ~Bool(Eval('boms', [])),
            })
Beispiel #17
0
class PatientGeneticRisk(ModelSQL, ModelView):
    'Patient Genetic Information'
    __name__ = 'gnuhealth.patient.genetic.risk'

    patient = fields.Many2One('gnuhealth.patient', 'Patient', select=True)
    disease_gene = fields.Many2One('gnuhealth.disease.gene',
        'Gene', required=True)
    natural_variant = fields.Many2One('gnuhealth.gene.variant', 'Variant',
        domain=[('name', '=', Eval('disease_gene'))],
        depends=['disease_gene'])
    variant_phenotype = fields.Many2One('gnuhealth.gene.variant.phenotype',\
        'Phenotype',
        domain=[('variant', '=', Eval('natural_variant'))],
        depends=['natural_variant'])
    onset = fields.Integer('Onset', help="Age in years")

    notes = fields.Char("Notes")

    healthprof = fields.Many2One(
        'gnuhealth.healthprofessional', 'Health prof',
        help="Health professional")

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

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

    @classmethod
    def create_genetics_pol(cls,genetic_info):
        """ Adds an entry in the person Page of Life
            related to this genetic finding
        """
        Pol = Pool().get('gnuhealth.pol')
        pol = []


        vals = {
            'page': str(uuid4()),
            'person': genetic_info.patient.name.id,
            'age': genetic_info.onset and str(genetic_info.onset) + 'y' or '',
            'federation_account': genetic_info.patient.name.federation_account,
            'page_type':'medical',
            'medical_context':'genetics',
            'relevance':'important',
            'gene':genetic_info.disease_gene.rec_name,
            'natural_variant':genetic_info.natural_variant and \
                genetic_info.natural_variant.aa_change,
            'summary': genetic_info.notes,
            'author': genetic_info.healthprof and genetic_info.healthprof.name.rec_name,
            'node': genetic_info.institution and genetic_info.institution.name.rec_name
            }
        if (genetic_info.variant_phenotype):
            vals['health_condition_text'] = vals['health_condition_text'] = \
                genetic_info.variant_phenotype.phenotype.rec_name

        pol.append(vals)
        Pol.create(pol)

    @classmethod
    def create(cls, vlist):

        # Execute first the creation of PoL
        genetic_info = super(PatientGeneticRisk, cls).create(vlist)

        cls.create_genetics_pol(genetic_info[0])

        return genetic_info

    @classmethod
    def search_rec_name(cls, name, clause):
        if clause[1].startswith('!') or clause[1].startswith('not '):
            bool_op = 'AND'
        else:
            bool_op = 'OR'
        return [bool_op,
            ('patient',) + tuple(clause[1:]),
            ('disease_gene',) + tuple(clause[1:]),
            ('variant_phenotype',) + tuple(clause[1:]),
            ]
Beispiel #18
0
                    config.payment_group_sequence.id)

        return super(Group, cls).create(vlist)

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


_STATES = {
    'readonly': Eval('state') != 'draft',
}
_DEPENDS = ['state']


class Payment(Workflow, ModelSQL, ModelView):
    'Payment'
    __name__ = 'account.payment'
    company = fields.Many2One(
        'company.company',
        'Company',
        required=True,
        select=True,
        states=_STATES,
        domain=[
            ('id', If(Eval('context', {}).contains('company'), '=',
Beispiel #19
0
class SaleLine:
    __name__ = 'sale.line'

    is_round_off = fields.Boolean('Round Off', readonly=True)

    delivery_mode = fields.Selection(
        [
            (None, ''),
            ('pick_up', 'Pick Up'),
            ('ship', 'Ship'),
        ],
        'Delivery Mode',
        states={
            'invisible':
            Eval('type') != 'line',
            'required':
            And(Eval('type') == 'line', Bool(Eval('product_type_is_goods')))
        },
        depends=['type', 'product_type_is_goods'])

    product_type_is_goods = fields.Function(
        fields.Boolean('Product Type is Goods?'), 'get_product_type_is_goods')

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

        TableHandler = backend.get('TableHandler')
        cursor = Transaction().cursor

        table = TableHandler(cursor, cls, module_name)

        table.not_null_action('delivery_mode', action='remove')

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

        # Hide product and unit fields.
        cls.product.states['invisible'] |= Bool(Eval('is_round_off'))
        cls.unit.states['invisible'] |= Bool(Eval('is_round_off'))
        cls.delivery_mode.states['invisible'] |= Bool(Eval('is_round_off'))
        cls.product.depends.insert(0, 'is_round_off')
        cls.unit.depends.insert(0, 'is_round_off')

    @fields.depends('product', 'unit', 'quantity', '_parent_sale.party',
                    '_parent_sale.currency', '_parent_sale.sale_date',
                    'delivery_mode', '_parent_sale.channel',
                    '_parent_sale.shipment_address', 'warehouse',
                    '_parent_sale.warehouse')
    def on_change_delivery_mode(self):
        """
        This method can be overridden by downstream modules to make changes
        according to delivery mode. Like change taxes according to delivery
        mode.
        """
        return {}

    @staticmethod
    def default_is_round_off():
        return False

    def get_invoice_line(self, invoice_type):
        SaleConfiguration = Pool().get('sale.configuration')
        InvoiceLine = Pool().get('account.invoice.line')

        if not self.is_round_off:
            return super(SaleLine, self).get_invoice_line(invoice_type)

        # The line is a round off line and apply the logic here.

        # Check if the invoice line already exists for the sale line
        # If yes, then no line needs to be created
        # XXX: What if the invoice was cancelled ?
        if self.invoice_lines:
            return []

        round_down_account = SaleConfiguration(1).round_down_account
        if not round_down_account:
            self.raise_user_error(
                '''Set round down account from Sale Configuration to
                add round off line''')

        invoice_line = InvoiceLine()
        invoice_line.origin = self
        invoice_line.account = round_down_account
        invoice_line.unit_price = self.unit_price
        invoice_line.description = self.description

        # For positive sales transactions (where the order is effectively
        # a positive total), the round_down is applied on out_invoice
        # and if overall order total is negative, then the round_down is
        # tied to credit note.
        if self.sale.total_amount >= Decimal('0'):
            if invoice_type == 'out_credit_note':
                # positive order looking for credit note
                return []
            invoice_line.quantity = self.quantity
        else:
            if invoice_type == 'out_invoice':
                # negative order looking for invoice
                return []
            invoice_line.quantity = -self.quantity

        return [invoice_line]

    @staticmethod
    def default_delivery_mode():
        Channel = Pool().get('sale.channel')
        User = Pool().get('res.user')

        user = User(Transaction().user)
        sale_channel = user.current_channel
        if Transaction().context.get('current_sale_channel'):
            sale_channel = Channel(
                Transaction().context.get('current_sale_channel'))
        return sale_channel and sale_channel.delivery_mode

    def get_warehouse(self, name):
        """
        Return the warehouse from the channel for orders being picked up and the
        backorder warehouse for orders with ship.
        """
        if self.delivery_mode == 'ship':
            return self.sale.channel.ship_from_warehouse.id
        return super(SaleLine, self).get_warehouse(name)

    def serialize(self, purpose=None):
        """
        Serialize for the purpose of POS
        """
        if purpose == 'pos':
            return {
                'id': self.id,
                'description': self.description,
                'product': self.product and {
                    'id':
                    self.product.id,
                    'code':
                    self.product.code,
                    'rec_name':
                    self.product.rec_name,
                    'default_image':
                    self.product.default_image
                    and self.product.default_image.id,
                },
                'unit': self.unit and {
                    'id': self.unit.id,
                    'rec_name': self.unit.rec_name,
                },
                'unit_price': self.unit_price,
                'quantity': self.quantity,
                'amount': self.amount,
                'delivery_mode': self.delivery_mode
            }
        elif hasattr(super(SaleLine, self), 'serialize'):
            return super(SaleLine, self).serialize(purpose)  # pragma: no cover

    def get_product_type_is_goods(self, name):
        """
        Return True if product is of type goods
        """
        if self.product and self.product.type == 'goods':
            return True
        return False
Beispiel #20
0
class Payment(Workflow, ModelSQL, ModelView):
    'Payment'
    __name__ = 'account.payment'
    company = fields.Many2One(
        'company.company',
        'Company',
        required=True,
        select=True,
        states=_STATES,
        domain=[
            ('id', If(Eval('context', {}).contains('company'), '=',
                      '!='), Eval('context', {}).get('company', -1)),
        ],
        depends=_DEPENDS)
    journal = fields.Many2One('account.payment.journal',
                              'Journal',
                              required=True,
                              states=_STATES,
                              domain=[
                                  ('company', '=', Eval('company', -1)),
                              ],
                              depends=_DEPENDS + ['company'])
    currency = fields.Function(fields.Many2One('currency.currency',
                                               'Currency'),
                               'on_change_with_currency',
                               searcher='search_currency')
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
                                      'on_change_with_currency_digits')
    kind = fields.Selection(KINDS,
                            'Kind',
                            required=True,
                            states=_STATES,
                            depends=_DEPENDS)
    party = fields.Many2One('party.party',
                            'Party',
                            required=True,
                            states=_STATES,
                            depends=_DEPENDS)
    date = fields.Date('Date', required=True, states=_STATES, depends=_DEPENDS)
    amount = fields.Numeric('Amount',
                            required=True,
                            digits=(16, Eval('currency_digits', 2)),
                            states=_STATES,
                            depends=_DEPENDS + ['currency_digits'])
    line = fields.Many2One(
        'account.move.line',
        'Line',
        ondelete='RESTRICT',
        domain=[
            ('move.company', '=', Eval('company', -1)),
            If(
                Eval('kind') == 'receivable',
                ['OR', ('debit', '>', 0), ('credit', '<', 0)],
                ['OR', ('credit', '>', 0), ('debit', '<', 0)],
            ),
            [
                'OR',
                ('account.type.receivable', '=', True),
                ('account.type.payable', '=', True),
            ],
            ('party', 'in', [Eval('party', None), None]),
            If(
                Eval('state') == 'draft', [
                    ('reconciliation', '=', None),
                ], []),
            [
                'OR',
                ('second_currency', '=', Eval('currency', None)),
                [
                    ('account.company.currency', '=', Eval('currency', None)),
                    ('second_currency', '=', None),
                ],
            ],
            ('move_state', '=', 'posted'),
        ],
        states=_STATES,
        depends=_DEPENDS + ['party', 'currency', 'kind', 'company'])
    description = fields.Char('Description', states=_STATES, depends=_DEPENDS)
    origin = fields.Reference("Origin",
                              selection='get_origin',
                              select=True,
                              states={
                                  'readonly': Eval('state') != 'draft',
                              },
                              depends=['state'])
    group = fields.Many2One('account.payment.group',
                            'Group',
                            readonly=True,
                            ondelete='RESTRICT',
                            states={
                                'required':
                                Eval('state').in_(['processing', 'succeeded']),
                            },
                            domain=[
                                ('company', '=', Eval('company', -1)),
                            ],
                            depends=['state', 'company'])
    state = fields.Selection([
        ('draft', 'Draft'),
        ('approved', 'Approved'),
        ('processing', 'Processing'),
        ('succeeded', 'Succeeded'),
        ('failed', 'Failed'),
    ],
                             'State',
                             readonly=True,
                             select=True)

    @classmethod
    def __setup__(cls):
        super(Payment, cls).__setup__()
        cls._order.insert(0, ('date', 'DESC'))
        cls._transitions |= set((
            ('draft', 'approved'),
            ('approved', 'processing'),
            ('processing', 'succeeded'),
            ('processing', 'failed'),
            ('approved', 'draft'),
            ('succeeded', 'failed'),
            ('failed', 'succeeded'),
        ))
        cls._buttons.update({
            'draft': {
                'invisible': Eval('state') != 'approved',
                'icon': 'tryton-back',
                'depends': ['state'],
            },
            'approve': {
                'invisible': Eval('state') != 'draft',
                'icon': 'tryton-forward',
                'depends': ['state'],
            },
            'succeed': {
                'invisible': ~Eval('state').in_(['processing', 'failed']),
                'icon': 'tryton-ok',
                'depends': ['state'],
            },
            'fail': {
                'invisible': ~Eval('state').in_(['processing', 'succeeded']),
                'icon': 'tryton-cancel',
                'depends': ['state'],
            },
        })
        cls.__rpc__.update({
            'approve':
            RPC(readonly=False, instantiate=0, fresh_session=True),
        })

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

    @staticmethod
    def default_kind():
        return 'payable'

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

    @staticmethod
    def default_state():
        return 'draft'

    @fields.depends('journal')
    def on_change_with_currency(self, name=None):
        if self.journal:
            return self.journal.currency.id

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

    @classmethod
    def search_currency(cls, name, clause):
        return [('journal.' + clause[0], ) + tuple(clause[1:])]

    @fields.depends('kind')
    def on_change_kind(self):
        self.line = None

    @fields.depends('party')
    def on_change_party(self):
        self.line = None

    @fields.depends('line')
    def on_change_line(self):
        if self.line:
            self.date = self.line.maturity_date
            self.amount = self.line.payment_amount

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

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

    @classmethod
    def delete(cls, payments):
        for payment in payments:
            if payment.state != 'draft':
                raise AccessError(
                    gettext('account_payment.msg_payment_delete_draft',
                            payment=payment.rec_name))
        super(Payment, cls).delete(payments)

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

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

    @classmethod
    @Workflow.transition('processing')
    def process(cls, payments, group):
        pool = Pool()
        Group = pool.get('account.payment.group')
        if payments:
            group = group()
            cls.write(payments, {
                'group': group.id,
            })
            process_method = getattr(
                Group, 'process_%s' % group.journal.process_method, None)
            if process_method:
                process_method(group)
                group.save()
            return group

    @classmethod
    @ModelView.button
    @Workflow.transition('succeeded')
    def succeed(cls, payments):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('failed')
    def fail(cls, payments):
        pass
Beispiel #21
0
class Year(Workflow, ModelSQL, ModelView):
    'Tmi Year'
    __name__ = 'tmi.year'
    name = fields.Char('Name', size=None, required=True, depends=DEPENDS)
    start_date = fields.Date('Starting Date',
                             required=True,
                             states=STATES,
                             domain=[('start_date', '<=',
                                      Eval('end_date', None))],
                             depends=DEPENDS + ['end_date'])
    end_date = fields.Date('Ending Date',
                           required=True,
                           states=STATES,
                           domain=[('end_date', '>=', Eval('start_date',
                                                           None))],
                           depends=DEPENDS + ['start_date'])
    periods = fields.One2Many('tmi.period',
                              'year',
                              'Periods',
                              states=STATES,
                              depends=DEPENDS)
    state = fields.Selection([
        ('open', 'Open'),
        ('close', 'Close'),
    ],
                             'State',
                             readonly=True,
                             required=True)
    icon = fields.Function(fields.Char("Icon"), 'get_icon')

    @classmethod
    def __setup__(cls):
        super(Year, cls).__setup__()
        cls._order.insert(0, ('start_date', 'ASC'))
        cls._error_messages.update({
            'no_year_date':
            'No year defined for "%s".',
            'year_overlaps': ('Year "%(first)s" and '
                              '"%(second)s" overlap.'),
            'close_error': ('You can not close year "%s" until you '
                            'close all previous years.'),
            'reopen_error': ('You can not reopen year "%s" until '
                             'you reopen all later years.'),
        })
        cls._transitions |= set((
            ('open', 'close'),
            ('close', 'open'),
        ))
        cls._buttons.update({
            'create_period': {
                'invisible': ((Eval('state') != 'open')
                              | Eval('periods', [0])),
                'depends': ['state'],
            },
            'close': {
                'invisible': Eval('state') != 'open',
                'depends': ['state'],
            },
            'reopen': {
                'invisible': Eval('state') != 'close',
                'depends': ['state'],
            },
        })

    @staticmethod
    def default_state():
        return 'open'

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

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

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

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

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

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

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

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

    @classmethod
    @ModelView.button
    @Workflow.transition('close')
    def close(cls, years):
        '''
        Close a year
        '''
        pool = Pool()
        Period = pool.get('tmi.period')

        transaction = Transaction()
        database = transaction.database
        connection = transaction.connection

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

        for year in years:
            if cls.search([
                ('end_date', '<=', year.start_date),
                ('state', '=', 'open'),
            ]):
                cls.raise_user_error('close_error', (year.rec_name, ))

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

    @classmethod
    @ModelView.button
    @Workflow.transition('open')
    def reopen(cls, years):
        '''
        Re-open a year
        '''

        for year in years:
            if cls.search([
                ('start_date', '>=', year.end_date),
                ('state', '!=', 'open'),
            ]):
                cls.raise_user_error('reopen_error')
Beispiel #22
0
class Group(ModelSQL, ModelView):
    'Payment Group'
    __name__ = 'account.payment.group'
    _rec_name = 'number'
    number = fields.Char('Number', required=True, readonly=True)
    company = fields.Many2One(
        'company.company',
        'Company',
        required=True,
        readonly=True,
        select=True,
        domain=[
            ('id', If(Eval('context', {}).contains('company'), '=',
                      '!='), Eval('context', {}).get('company', -1)),
        ])
    journal = fields.Many2One('account.payment.journal',
                              'Journal',
                              required=True,
                              readonly=True,
                              domain=[
                                  ('company', '=', Eval('company', -1)),
                              ],
                              depends=['company'])
    kind = fields.Selection(KINDS, 'Kind', required=True, readonly=True)
    payments = fields.One2Many('account.payment',
                               'group',
                               'Payments',
                               readonly=True)

    @classmethod
    def __register__(cls, module_name):
        table_h = cls.__table_handler__(module_name)

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

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

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

        vlist = [v.copy() for v in vlist]
        config = Config(1)
        for values in vlist:
            if values.get('number') is None:
                values['number'] = Sequence.get_id(
                    config.payment_group_sequence.id)

        return super(Group, cls).create(vlist)

    @classmethod
    def copy(cls, groups, default=None):
        if default is None:
            default = {}
        else:
            default = default.copy()
        default.setdefault('payments', None)
        return super(Group, cls).copy(groups, default=default)
Beispiel #23
0
class PaymentTermLine(sequence_ordered(), ModelSQL, ModelView):
    'Payment Term Line'
    __name__ = 'account.invoice.payment_term.line'
    payment = fields.Many2One('account.invoice.payment_term', 'Payment Term',
            required=True, ondelete="CASCADE")
    type = fields.Selection([
            ('fixed', 'Fixed'),
            ('percent', 'Percentage on Remainder'),
            ('percent_on_total', 'Percentage on Total'),
            ('remainder', 'Remainder'),
            ], 'Type', required=True)
    ratio = fields.Numeric('Ratio', digits=(14, 10),
        states={
            'invisible': ~Eval('type').in_(['percent', 'percent_on_total']),
            'required': Eval('type').in_(['percent', 'percent_on_total']),
            }, depends=['type'])
    divisor = fields.Numeric('Divisor', digits=(10, 14),
        states={
            'invisible': ~Eval('type').in_(['percent', 'percent_on_total']),
            'required': Eval('type').in_(['percent', 'percent_on_total']),
            }, depends=['type'])
    amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
        states={
            'invisible': Eval('type') != 'fixed',
            'required': Eval('type') == 'fixed',
            }, depends=['type', 'currency_digits'])
    currency = fields.Many2One('currency.currency', 'Currency',
        states={
            'invisible': Eval('type') != 'fixed',
            'required': Eval('type') == 'fixed',
            }, depends=['type'])
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
        'on_change_with_currency_digits')
    relativedeltas = fields.One2Many(
        'account.invoice.payment_term.line.delta', 'line', 'Deltas')

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

        # Migration from 3.8: rename percentage into ratio
        if table.column_exist('percentage'):
            cursor.execute(*sql_table.update(
                    columns=[sql_table.ratio],
                    values=[sql_table.percentage / 100]))
            table.drop_column('percentage')

    @staticmethod
    def default_currency_digits():
        return 2

    @staticmethod
    def default_type():
        return 'remainder'

    @classmethod
    def default_relativedeltas(cls):
        if Transaction().user == 0:
            return []
        return [{}]

    @fields.depends('type')
    def on_change_type(self):
        if self.type != 'fixed':
            self.amount = Decimal('0.0')
            self.currency = None
        if self.type not in ('percent', 'percent_on_total'):
            self.ratio = Decimal('0.0')
            self.divisor = Decimal('0.0')

    @fields.depends('ratio')
    def on_change_ratio(self):
        if not self.ratio:
            self.divisor = Decimal('0.0')
        else:
            self.divisor = self.round(1 / self.ratio,
                self.__class__.divisor.digits[1])

    @fields.depends('divisor')
    def on_change_divisor(self):
        if not self.divisor:
            self.ratio = Decimal('0.0')
        else:
            self.ratio = self.round(1 / self.divisor,
                self.__class__.ratio.digits[1])

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

    def get_date(self, date):
        for relativedelta_ in self.relativedeltas:
            date += relativedelta_.get()
        return date

    def get_value(self, remainder, amount, currency):
        Currency = Pool().get('currency.currency')
        if self.type == 'fixed':
            fixed = Currency.compute(self.currency, self.amount, currency)
            return fixed.copy_sign(amount)
        elif self.type == 'percent':
            return currency.round(remainder * self.ratio)
        elif self.type == 'percent_on_total':
            return currency.round(amount * self.ratio)
        elif self.type == 'remainder':
            return currency.round(remainder)
        return None

    @staticmethod
    def round(number, digits):
        quantize = Decimal(10) ** -Decimal(digits)
        return Decimal(number).quantize(quantize)

    @classmethod
    def validate(cls, lines):
        super(PaymentTermLine, cls).validate(lines)
        cls.check_ratio_and_divisor(lines)

    @classmethod
    def check_ratio_and_divisor(cls, lines):
        "Check consistency between ratio and divisor"
        # Use a copy because on_change will change the records
        for line in cls.browse(lines):
            if line.type not in ('percent', 'percent_on_total'):
                continue
            if line.ratio is None or line.divisor is None:
                raise PaymentTermValidationError(
                    gettext('account_invoice'
                        '.msg_payment_term_invalid_ratio_divisor',
                        line=line.rec_name))
            ratio = line.ratio
            divisor = line.divisor
            line.on_change_ratio()
            line.on_change_divisor()
            if (line.divisor != divisor) or (line.ratio != ratio):
                raise PaymentTermValidationError(
                    gettext('account_invoice'
                        '.msg_payment_term_invalid_ratio_divisor',
                        line=line.rec_name))
Beispiel #24
0
class SaleLine:
    "SaleLine"
    __name__ = 'sale.line'

    gift_card_delivery_mode = fields.Function(
        fields.Selection([
            ('virtual', 'Virtual'),
            ('physical', 'Physical'),
            ('combined', 'Combined'),
        ],
                         'Gift Card Delivery Mode',
                         states={
                             'invisible': ~Bool(Eval('is_gift_card')),
                         },
                         depends=['is_gift_card']),
        'on_change_with_gift_card_delivery_mode')

    is_gift_card = fields.Function(fields.Boolean('Gift Card'),
                                   'on_change_with_is_gift_card')
    gift_cards = fields.One2Many('gift_card.gift_card',
                                 "sale_line",
                                 "Gift Cards",
                                 readonly=True)
    message = fields.Text("Message",
                          states={'invisible': ~Bool(Eval('is_gift_card'))})

    recipient_email = fields.Char(
        "Recipient Email",
        states={
            'invisible':
            ~(Bool(Eval('is_gift_card')) &
              (Eval('gift_card_delivery_mode').in_(['virtual', 'combined']))),
            'required':
            (Bool(Eval('is_gift_card')) &
             (Eval('gift_card_delivery_mode').in_(['virtual', 'combined']))),
        },
        depends=['gift_card_delivery_mode', 'is_gift_card'])

    recipient_name = fields.Char("Recipient Name",
                                 states={
                                     'invisible': ~Bool(Eval('is_gift_card')),
                                 },
                                 depends=['is_gift_card'])
    allow_open_amount = fields.Function(
        fields.Boolean("Allow Open Amount?",
                       states={'invisible': ~Bool(Eval('is_gift_card'))},
                       depends=['is_gift_card']),
        'on_change_with_allow_open_amount')

    gc_price = fields.Many2One(
        'product.product.gift_card.price',
        "Gift Card Price",
        states={
            'required':
            (~Bool(Eval('allow_open_amount')) & Bool(Eval('is_gift_card'))),
            'invisible':
            ~(~Bool(Eval('allow_open_amount')) & Bool(Eval('is_gift_card')))
        },
        depends=['allow_open_amount', 'is_gift_card', 'product'],
        domain=[('product', '=', Eval('product'))])

    @fields.depends('product')
    def on_change_with_allow_open_amount(self, name=None):
        if self.product:
            return self.product.allow_open_amount

    @fields.depends('gc_price', 'unit_price')
    def on_change_gc_price(self, name=None):
        res = {}
        if self.gc_price:
            res['unit_price'] = self.gc_price.price
        return res

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

        cls.unit_price.states['readonly'] = (~Bool(Eval('allow_open_amount'))
                                             & Bool(Eval('is_gift_card')))

        cls._error_messages.update({
            'amounts_out_of_range':
            'Gift card amount must be within %s %s and %s %s'
        })

    @fields.depends('product', 'is_gift_card')
    def on_change_with_gift_card_delivery_mode(self, name=None):
        """
        Returns delivery mode of the gift card product
        """
        if not (self.product and self.is_gift_card):
            return None

        return self.product.gift_card_delivery_mode

    @classmethod
    def copy(cls, lines, default=None):
        if default is None:
            default = {}
        default['gift_cards'] = None
        return super(SaleLine, cls).copy(lines, default=default)

    @fields.depends('product')
    def on_change_with_is_gift_card(self, name=None):
        """
        Returns boolean value to tell if product is gift card or not
        """
        return self.product and self.product.is_gift_card

    def get_invoice_line(self, invoice_type):
        """
        Pick up liability account from gift card configuration for invoices
        """
        GiftCardConfiguration = Pool().get('gift_card.configuration')

        lines = super(SaleLine, self).get_invoice_line(invoice_type)

        if lines and self.is_gift_card:
            liability_account = GiftCardConfiguration(1).liability_account

            if not liability_account:
                self.raise_user_error(
                    "Liability Account is missing from Gift Card "
                    "Configuration")

            for invoice_line in lines:
                invoice_line.account = liability_account

        return lines

    @fields.depends('is_gift_card')
    def on_change_is_gift_card(self):
        ModelData = Pool().get('ir.model.data')

        if self.is_gift_card:
            return {
                'product': None,
                'description': 'Gift Card',
                'unit': ModelData.get_id('product', 'uom_unit'),
            }
        return {
            'description': None,
            'unit': None,
        }

    def create_gift_cards(self):
        '''
        Create the actual gift card for this line
        '''
        GiftCard = Pool().get('gift_card.gift_card')

        if not self.is_gift_card:
            # Not a gift card line
            return None

        product = self.product

        if product.allow_open_amount and not (product.gc_min <= self.unit_price
                                              <= product.gc_max):
            self.raise_user_error("amounts_out_of_range",
                                  (self.sale.currency.code, product.gc_min,
                                   self.sale.currency.code, product.gc_max))

        # XXX: Do not consider cancelled ones in the gift cards.
        # card could have been cancelled for reasons like wrong message ?
        quantity_created = len(self.gift_cards)

        if self.sale.gift_card_method == 'order':
            quantity = self.quantity - quantity_created
        else:
            # On invoice paid
            quantity_paid = 0
            for invoice_line in self.invoice_lines:
                if invoice_line.invoice.state == 'paid':
                    invoice_line.quantity
                    quantity_paid += invoice_line.quantity

            # Remove already created gift cards
            quantity = quantity_paid - quantity_created

        if not quantity > 0:
            # No more gift cards to create
            return

        gift_cards = GiftCard.create([{
            'amount':
            self.unit_price,
            'sale_line':
            self.id,
            'message':
            self.message,
            'recipient_email':
            self.recipient_email,
            'recipient_name':
            self.recipient_name,
            'origin':
            '%s,%d' % (self.sale.__name__, self.sale.id),
        } for each in range(0, int(quantity))])

        GiftCard.activate(gift_cards)

        return gift_cards
Beispiel #25
0
class MoveLine:
    __metaclass__ = PoolMeta
    __name__ = 'account.move.line'
    payment_amount = fields.Function(fields.Numeric(
        'Payment Amount',
        digits=(16,
                If(Bool(Eval('second_currency_digits')),
                   Eval('second_currency_digits', 2),
                   Eval('currency_digits', 2))),
        states={
            'invisible': ~Eval('payment_kind'),
        },
        depends=['payment_kind', 'second_currency_digits', 'currency_digits']),
                                     'get_payment_amount',
                                     searcher='search_payment_amount')
    payments = fields.One2Many('account.payment',
                               'line',
                               'Payments',
                               readonly=True,
                               states={
                                   'invisible': ~Eval('payment_kind'),
                               },
                               depends=['payment_kind'])
    payment_kind = fields.Function(fields.Selection([
        (None, ''),
    ] + KINDS, 'Payment Kind'),
                                   'get_payment_kind',
                                   searcher='search_payment_kind')

    @classmethod
    def __setup__(cls):
        super(MoveLine, cls).__setup__()
        cls._buttons.update({
            'pay': {
                'invisible': ~Eval('payment_kind').in_(dict(KINDS).keys()),
            },
        })

    @classmethod
    def get_payment_amount(cls, lines, name):
        amounts = {}
        for line in lines:
            if line.account.kind not in ('payable', 'receivable'):
                amounts[line.id] = None
                continue
            if line.second_currency:
                amount = abs(line.amount_second_currency)
            else:
                amount = abs(line.credit - line.debit)

            for payment in line.payments:
                if payment.state != 'failed':
                    amount -= payment.amount

            amounts[line.id] = amount
        return amounts

    @classmethod
    def search_payment_amount(cls, name, clause):
        pool = Pool()
        Payment = pool.get('account.payment')
        Account = pool.get('account.account')
        _, operator, value = clause
        Operator = fields.SQL_OPERATORS[operator]
        table = cls.__table__()
        payment = Payment.__table__()
        account = Account.__table__()

        payment_amount = Sum(Coalesce(payment.amount, 0))
        main_amount = Abs(table.credit - table.debit) - payment_amount
        second_amount = Abs(table.amount_second_currency) - payment_amount
        amount = Case((table.second_currency == Null, main_amount),
                      else_=second_amount)
        value = cls.payment_amount.sql_format(value)

        query = table.join(
            payment,
            type_='LEFT',
            condition=(table.id == payment.line) &
            (payment.state != 'failed')).join(
                account, condition=table.account == account.id).select(
                    table.id,
                    where=account.kind.in_(['payable', 'receivable']),
                    group_by=(table.id, account.kind, table.second_currency),
                    having=Operator(amount, value))
        return [('id', 'in', query)]

    def get_payment_kind(self, name):
        return self.account.kind if self.account.kind in dict(KINDS) else None

    @classmethod
    def search_payment_kind(cls, name, clause):
        return [('account.kind', ) + tuple(clause[1:])]

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

    @classmethod
    @ModelView.button_action('account_payment.act_pay_line')
    def pay(cls, lines):
        pass
class GiftCard(Workflow, ModelSQL, ModelView):
    "Gift Card"
    __name__ = 'gift_card.gift_card'
    _rec_name = 'number'

    number = fields.Char('Number',
                         select=True,
                         readonly=True,
                         required=True,
                         help='Number of the gift card')
    origin = fields.Reference('Origin',
                              selection='get_origin',
                              select=True,
                              states={
                                  'readonly': Eval('state') != 'draft',
                              },
                              depends=['state'])
    currency = fields.Many2One('currency.currency',
                               'Currency',
                               required=True,
                               states={'readonly': Eval('state') != 'draft'},
                               depends=['state'])
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
                                      'on_change_with_currency_digits')
    amount = fields.Numeric('Amount',
                            digits=(16, Eval('currency_digits', 2)),
                            states={'readonly': Eval('state') != 'draft'},
                            depends=['state', 'currency_digits'],
                            required=True)

    amount_authorized = fields.Function(
        fields.Numeric("Amount Authorized",
                       digits=(16, Eval('currency_digits', 2)),
                       depends=['currency_digits']), 'get_amount')
    amount_captured = fields.Function(
        fields.Numeric("Amount Captured",
                       digits=(16, Eval('currency_digits', 2)),
                       depends=['currency_digits']), 'get_amount')

    amount_available = fields.Function(
        fields.Numeric("Amount Available",
                       digits=(16, Eval('currency_digits', 2)),
                       depends=['currency_digits']), 'get_amount')
    state = fields.Selection([
        ('draft', 'Draft'),
        ('active', 'Active'),
        ('canceled', 'Canceled'),
        ('used', 'Used'),
    ],
                             'State',
                             readonly=True,
                             required=True)

    sale_line = fields.Many2One('sale.line', "Sale Line", readonly=True)

    sale = fields.Function(fields.Many2One('sale.sale', "Sale"), 'get_sale')
    payment_transactions = fields.One2Many("payment_gateway.transaction",
                                           "gift_card",
                                           "Payment Transactions",
                                           readonly=True)
    message = fields.Text("Message")
    recipient_email = fields.Char(
        "Recipient Email", states={'readonly': Eval('state') != 'draft'})

    recipient_name = fields.Char("Recipient Name",
                                 states={'readonly': Eval('state') != 'draft'})

    is_email_sent = fields.Boolean("Is Email Sent ?", readonly=True)
    comment = fields.Text('Comment')

    def get_sale(self, name):
        """
        Return sale for gift card using sale line associated with it
        """
        return self.sale_line and self.sale_line.sale.id or None

    @staticmethod
    def default_currency():
        """
        Set currency of current company as default currency
        """
        Company = Pool().get('company.company')

        return Transaction().context.get('company') and \
            Company(Transaction().context.get('company')).currency.id or None

    def get_amount(self, name):
        """
        Returns authorzied, captured and available amount for the gift card
        """
        PaymentTransaction = Pool().get('payment_gateway.transaction')

        if name == 'amount_authorized':
            return sum([
                t.amount for t in PaymentTransaction.search([(
                    'state', '=', 'authorized'), ('gift_card', '=', self.id)])
            ])

        if name == 'amount_captured':
            return sum([
                t.amount for t in PaymentTransaction.search([(
                    'state', 'in',
                    ['posted', 'done']), ('gift_card', '=', self.id)])
            ])

        if name == 'amount_available':
            return self.amount - sum([
                t.amount for t in PaymentTransaction.search([(
                    'state', 'in', ['authorized', 'posted', 'done']
                ), ('gift_card', '=', self.id)])
            ])

    @staticmethod
    def default_state():
        return 'draft'

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

    @classmethod
    def __setup__(cls):
        super(GiftCard, cls).__setup__()
        table = cls.__table__()
        cls._sql_constraints = [('number_uniq', Unique(table, table.number),
                                 'The number of the gift card must be unique.')
                                ]
        cls._error_messages.update({
            'deletion_not_allowed':
            "Gift cards can not be deleted in active state"
        })
        cls._transitions |= set((
            ('draft', 'active'),
            ('active', 'canceled'),
            ('draft', 'canceled'),
            ('canceled', 'draft'),
        ))
        cls._buttons.update({
            'cancel': {
                'invisible': ~Eval('state').in_(['draft', 'active']),
            },
            'draft': {
                'invisible':
                ~Eval('state').in_(['canceled']),
                'icon':
                If(
                    Eval('state') == 'cancel', 'tryton-clear',
                    'tryton-go-previous'),
            },
            'activate': {
                'invisible': Eval('state') != 'draft',
            }
        })

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

        vlist = [x.copy() for x in vlist]
        for values in vlist:
            if not values.get('number'):
                values['number'] = Sequence.get_id(
                    Configuration(1).number_sequence.id)
        return super(GiftCard, cls).create(vlist)

    @classmethod
    def copy(cls, gift_cards, default=None):
        if default is None:
            default = {}
        default = default.copy()
        default['number'] = None
        default['sale_line'] = None
        default['state'] = cls.default_state()
        default['payment_transactions'] = None
        return super(GiftCard, cls).copy(gift_cards, default=default)

    @classmethod
    @ModelView.button
    @Workflow.transition('active')
    def activate(cls, gift_cards):
        """
        Set gift cards to active state
        """
        for gift_card in gift_cards:
            if gift_card.recipient_email and not gift_card.is_email_sent:
                gift_card.send_gift_card_as_email()

    @classmethod
    @ModelView.button
    @Workflow.transition('draft')
    def draft(cls, gift_cards):
        """
        Set gift cards back to draft state
        """
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('canceled')
    def cancel(cls, gift_cards):
        """
        Cancel gift cards
        """
        pass

    @classmethod
    def get_origin(cls):
        return [
            (None, ''),
            ('sale.sale', 'Sale'),
        ]

    @classmethod
    def delete(cls, gift_cards):
        """
        It should not be possible to delete gift cards in active state
        """

        for gift_card in gift_cards:
            if gift_card.state == 'active':
                cls.raise_user_error("deletion_not_allowed")

        return super(GiftCard, cls).delete(gift_cards)

    def _get_subject_for_email(self):
        """
        Returns the text to use as subject of email
        """
        return "Gift Card - %s" % self.number

    def _get_email_templates(self):
        """
        Returns a tuple of the form:

        (html_template, text_template)
        """
        env = Environment(
            loader=PackageLoader('trytond.modules.gift_card', 'emails'))
        return (env.get_template('gift_card_html.html'),
                env.get_template('gift_card_text.html'))

    def send_gift_card_as_email(self):
        """
        Send gift card as an attachment in the email
        """
        EmailQueue = Pool().get('email.queue')
        GiftCardReport = Pool().get('gift_card.gift_card', type='report')
        ModelData = Pool().get('ir.model.data')
        Group = Pool().get('res.group')

        group_id = ModelData.get_id("gift_card", "gift_card_email_receivers")
        bcc_emails = map(
            lambda user: user.email,
            filter(lambda user: user.email,
                   Group(group_id).users))

        if not self.recipient_email:  # pragma: no cover
            return

        # Try to generate report twice
        # This is needed as sometimes `unoconv` fails to convert report to pdf
        for try_count in range(2):
            try:
                val = GiftCardReport.execute([self.id], {})
                break
            except:  # pragma: no cover
                if try_count == 0:
                    continue
                else:
                    return

        subject = self._get_subject_for_email()
        html_template, text_template = self._get_email_templates()

        sender = config.get('email', 'from')

        email_gift_card = render_email(
            sender,
            self.recipient_email,
            subject,
            html_template=html_template,
            text_template=text_template,
            attachments={"%s.%s" % (val[3], val[0]): val[1]},
            card=self,
        )

        EmailQueue.queue_mail(sender, [self.recipient_email] + bcc_emails,
                              email_gift_card.as_string())
        self.is_email_sent = True
        self.save()
Beispiel #27
0
class Carrier(metaclass=PoolMeta):
    __name__ = 'carrier'
    weight_uom = fields.Many2One(
        'product.uom',
        'Weight Uom',
        domain=[('category', '=', Id('product', 'uom_cat_weight'))],
        states={
            'invisible': Eval('carrier_cost_method') != 'weight',
            'required': Eval('carrier_cost_method') == 'weight',
            'readonly': Bool(Eval('weight_price_list', [])),
        },
        depends=['carrier_cost_method', 'weight_price_list'],
        help="The unit of weight criteria of the price list.")
    weight_uom_digits = fields.Function(fields.Integer('Weight Uom Digits'),
                                        'on_change_with_weight_uom_digits')
    weight_currency = fields.Many2One(
        'currency.currency',
        'Currency',
        states={
            'invisible': Eval('carrier_cost_method') != 'weight',
            'required': Eval('carrier_cost_method') == 'weight',
            'readonly': Bool(Eval('weight_price_list', [])),
        },
        depends=['carrier_cost_method', 'weight_price_list'],
        help="The currency of the price.")
    weight_currency_digits = fields.Function(
        fields.Integer('Weight Currency Digits'),
        'on_change_with_weight_currency_digits')
    weight_price_list = fields.One2Many(
        'carrier.weight_price_list',
        'carrier',
        'Price List',
        states={
            'invisible': Eval('carrier_cost_method') != 'weight',
            'readonly': ~(Eval('weight_uom', 0) & Eval('weight_currency', 0)),
        },
        depends=['carrier_cost_method', 'weight_uom', 'weight_currency'],
        help="Add price to the carrier service.")

    @classmethod
    def __setup__(cls):
        super(Carrier, cls).__setup__()
        selection = ('weight', 'Weight')
        if selection not in cls.carrier_cost_method.selection:
            cls.carrier_cost_method.selection.append(selection)

    @staticmethod
    def default_weight_uom_digits():
        return 2

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

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

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

    def compute_weight_price(self, weight):
        "Compute price based on weight"
        for line in reversed(self.weight_price_list):
            if line.weight < weight:
                return line.price
        return Decimal(0)

    def get_sale_price(self):
        price, currency_id = super(Carrier, self).get_sale_price()
        if self.carrier_cost_method == 'weight':
            weight_price = Decimal(0)
            for weight in Transaction().context.get('weights', []):
                weight_price += self.compute_weight_price(weight)
            return weight_price, self.weight_currency.id
        return price, currency_id

    def get_purchase_price(self):
        price, currency_id = super(Carrier, self).get_purchase_price()
        if self.carrier_cost_method == 'weight':
            weight_price = Decimal(0)
            for weight in Transaction().context.get('weights', []):
                weight_price += self.compute_weight_price(weight)
            return weight_price, self.weight_currency.id
        return price, currency_id
class GiftCardRedeemStart(ModelView):
    "Gift Card Redeem Start View"
    __name__ = 'gift_card.redeem.start'

    description = fields.Text('Description', required=True)
    gateway = fields.Many2One('payment_gateway.gateway',
                              'Gateway',
                              required=True,
                              domain=[
                                  ('method', '=', 'gift_card'),
                              ])
    gift_card = fields.Many2One('gift_card.gift_card',
                                'Gift Card',
                                readonly=True)
    party = fields.Many2One('party.party', 'Party', required=True)
    amount = fields.Numeric('Amount', digits=(16, 2), required=True)
    address = fields.Many2One('party.address',
                              'Billing Address',
                              required=True,
                              domain=[('party', '=', Eval('party'))],
                              depends=['party'])
    currency = fields.Many2One('currency.currency', 'Currency', required=True)
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
                                      'on_change_with_currency_digits')

    @staticmethod
    def default_currency():
        """
        Set currency of current company as default currency
        """
        Company = Pool().get('company.company')

        return Transaction().context.get('company') and \
            Company(Transaction().context.get('company')).currency.id or None

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

    @fields.depends('party')
    def on_change_with_address(self, name=None):  # pragma: no cover
        """
        This method returns one of the following, once the party is set -:

        * If the party has invoice addresses, the first among them is shown by
          default.
        * If the party has no invoice addresses but has addresses, the first
          among those addresses is shown.
        * If the party has no addresses, the user gets to select them.
        """
        Address = Pool().get('party.address')

        if self.party is None:  # If the party is removed altogether
            return None

        try:
            address, = Address.search([('party', '=', self.party.id)],
                                      order=[('invoice', 'DESC')],
                                      limit=1)
        except ValueError:
            return None
        return address.id
Beispiel #29
0
class Complaint(Workflow, ModelSQL, ModelView):
    'Customer Complaint'
    __name__ = 'sale.complaint'
    _rec_name = 'number'

    _states = {
        'readonly': Eval('state') != 'draft',
        }
    _depends = ['state']

    number = fields.Char('Number', readonly=True, select=True)
    reference = fields.Char('Reference', select=True)
    date = fields.Date('Date', states=_states, depends=_depends)
    customer = fields.Many2One('party.party', 'Customer', required=True,
        states=_states, depends=_depends)
    address = fields.Many2One('party.address', 'Address',
        domain=[('party', '=', Eval('customer'))],
        states=_states, depends=_depends + ['customer'])
    company = fields.Many2One('company.company', 'Company', required=True,
        states=_states, depends=_depends)
    employee = fields.Many2One('company.employee', 'Employee',
        states=_states, depends=_depends)
    type = fields.Many2One('sale.complaint.type', 'Type', required=True,
        states=_states, depends=_depends)
    origin = fields.Reference('Origin', selection='get_origin',
        states={
            'readonly': ((Eval('state') != 'draft')
                | Bool(Eval('actions', [0]))),
            'required': Bool(Eval('origin_model')),
            },
        depends=['state', 'customer', 'origin_model', 'company'])
    origin_id = fields.Function(fields.Integer('Origin ID'),
        'on_change_with_origin_id')
    origin_model = fields.Function(fields.Char('Origin Model'),
        'on_change_with_origin_model')
    description = fields.Text('Description', states=_states, depends=_depends)
    actions = fields.One2Many('sale.complaint.action', 'complaint', 'Actions',
        states={
            'readonly': ((Eval('state') != 'draft')
                | (If(~Eval('origin_id', 0), 0, Eval('origin_id', 0)) <= 0)),
            },
        depends=['state', 'origin_model', 'origin_id'])
    state = fields.Selection([
            ('draft', 'Draft'),
            ('waiting', 'Waiting'),
            ('approved', 'Approved'),
            ('rejected', 'Rejected'),
            ('done', 'Done'),
            ('cancelled', 'Cancelled'),
            ], 'State', readonly=True, required=True)

    @classmethod
    def __setup__(cls):
        super(Complaint, cls).__setup__()
        cls._order.insert(0, ('date', 'DESC'))
        cls._error_messages.update({
                'delete_draft': ('Complaint "%s" must be in draft '
                    'to be deleted.'),
                })
        cls._transitions |= set((
                ('draft', 'waiting'),
                ('waiting', 'draft'),
                ('waiting', 'approved'),
                ('waiting', 'rejected'),
                ('approved', 'done'),
                ('draft', 'cancelled'),
                ('waiting', 'cancelled'),
                ('done', 'draft'),
                ('cancelled', 'draft'),
                ))
        cls._buttons.update({
                'cancel': {
                    'invisible': ~Eval('state').in_(['draft', 'waiting']),
                    },
                'draft': {
                    'invisible': ~Eval('state').in_(
                        ['waiting', 'done', 'cancelled']),
                    'icon': If(Eval('state').in_(['done', 'cancelled']),
                        'tryton-clear', 'tryton-go-previous'),
                    },
                'wait': {
                    'invisible': ~Eval('state').in_(['draft']),
                    },
                'approve': {
                    'invisible': (~Eval('state').in_(['waiting'])
                        & Eval('context', {}).get('groups', []).contains(
                            Id('sale', 'group_sale_admin'))),
                    },
                'reject': {
                    'invisible': (~Eval('state').in_(['waiting'])
                        & Eval('context', {}).get('groups', []).contains(
                            Id('sale', 'group_sale_admin'))),
                    },
                'process': {
                    'invisible': ~Eval('state').in_(['approved']),
                    },
                })

        origin_domain = []
        for model, domain in cls._origin_domains().iteritems():
            origin_domain = If(Eval('origin_model') == model,
                domain, origin_domain)
        cls.origin.domain = [origin_domain]

        actions_domains = cls._actions_domains()
        actions_domain = [('action', 'in', actions_domains.pop(None))]
        for model, actions in actions_domains.iteritems():
            actions_domain = If(Eval('origin_model') == model,
                [('action', 'in', actions)], actions_domain)
        cls.actions.domain = [actions_domain]

    @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')
                and not table_h.column_exist('number')):
            table_h.column_rename('reference', 'number')

        super(Complaint, cls).__register__(module_name)

    @classmethod
    def _origin_domains(cls):
        return {
            'sale.sale': [
                ('party', '=', Eval('customer')),
                ('company', '=', Eval('company')),
                ('state', 'in', ['confirmed', 'processing', 'done']),
                ],
            'sale.line': [
                ('sale.party', '=', Eval('customer')),
                ('sale.company', '=', Eval('company')),
                ('sale.state', 'in', ['confirmed', 'processing', 'done']),
                ],
            'account.invoice': [
                ('party', '=', Eval('customer')),
                ('company', '=', Eval('company')),
                ('type', '=', 'out'),
                ('state', 'in', ['posted', 'paid']),
                ],
            'account.invoice.line': [
                ('invoice.party', '=', Eval('customer')),
                ('invoice.company', '=', Eval('company')),
                ('invoice.type', '=', 'out'),
                ('invoice.state', 'in', ['posted', 'paid']),
                ],
            }

    @classmethod
    def _actions_domains(cls):
        return {
            None: [],
            'sale.sale': ['sale_return'],
            'sale.line': ['sale_return'],
            'account.invoice': ['credit_note'],
            'account.invoice.line': ['credit_note'],
            }

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

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

    @staticmethod
    def default_state():
        return 'draft'

    @fields.depends('type')
    def get_origin(self):
        if self.type:
            origin = self.type.origin
            return [('', ''), (origin.model, origin.name)]
        else:
            return []

    @fields.depends('origin')
    def on_change_with_origin_id(self, name=None):
        if self.origin:
            return self.origin.id

    @fields.depends('origin')
    def on_change_with_origin_model(self, name=None):
        if self.origin:
            return self.origin.__class__.__name__

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

        config = Configuration(1)
        vlist = [v.copy() for v in vlist]
        for values in vlist:
            if values.get('number') is None:
                values['number'] = Sequence.get_id(
                    config.complaint_sequence.id)
        return super(Complaint, cls).create(vlist)

    @classmethod
    def copy(cls, complaints, default=None):
        if default is None:
            default = {}
        default = default.copy()
        default['number'] = None
        return super(Complaint, cls).copy(complaints, default=default)

    @classmethod
    def delete(cls, complaints):
        for complaint in complaints:
            if complaint.state != 'draft':
                cls.raise_user_error('delete_draft', complaint.rec_name)
        super(Complaint, cls).delete(complaints)

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

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

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

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

    @classmethod
    @ModelView.button
    @Workflow.transition('rejected')
    def reject(cls, complaints):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('done')
    def process(cls, complaints):
        results = defaultdict(list)
        actions = defaultdict(list)
        for complaint in complaints:
            for action in complaint.actions:
                if action.result:
                    continue
                result = action.do()
                results[result.__class__].append(result)
                actions[result.__class__].append(action)
        for kls, records in results.iteritems():
            kls.save(records)
            for action, record in zip(actions[kls], records):
                action.result = record
        Action.save(sum(actions.values(), []))
Beispiel #30
0
class SaleOpportunityEmployeeMonthly(ModelSQL, ModelView):
    'Sale Opportunity per Employee per Month'
    __name__ = 'sale.opportunity_employee_monthly'
    year = fields.Char('Year')
    month = fields.Integer('Month')
    employee = fields.Many2One('company.employee', 'Employee')
    number = fields.Integer('Number')
    converted = fields.Integer('Converted')
    conversion_rate = fields.Function(
        fields.Float('Conversion Rate', help='In %'), 'get_conversion_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', help='In %'),
        'get_conversion_amount_rate')

    @classmethod
    def __setup__(cls):
        super(SaleOpportunityEmployeeMonthly, cls).__setup__()
        cls._order.insert(0, ('year', 'DESC'))
        cls._order.insert(1, ('month', 'DESC'))
        cls._order.insert(2, ('employee', 'ASC'))

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

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

    @classmethod
    def table_query(cls):
        Opportunity = Pool().get('sale.opportunity')
        type_id = FIELDS[cls.id._type].sql_type(cls.id)[0]
        type_year = FIELDS[cls.year._type].sql_type(cls.year)[0]
        return ('SELECT CAST(id AS ' + type_id + ') AS id, create_uid, '
                'create_date, write_uid, write_date, '
                'CAST(year AS ' + type_year + ') AS year, month, '
                'employee, company, number, converted, lost, amount, '
                'converted_amount '
                'FROM ('
                'SELECT EXTRACT(MONTH FROM start_date) + '
                'EXTRACT(YEAR FROM start_date) * 100 + '
                'employee * 1000000 AS id, '
                'MAX(create_uid) AS create_uid, '
                'MAX(create_date) AS create_date, '
                'MAX(write_uid) AS write_uid, '
                'MAX(write_date) AS write_date, '
                'EXTRACT(YEAR FROM start_date) AS year, '
                'EXTRACT(MONTH FROM start_date) AS month, '
                'employee, '
                'company, '
                'COUNT(1) AS number, '
                'SUM(CASE WHEN state IN (' +
                ','.join("'%s'" % x for x in cls._converted_state()) + ') '
                'THEN 1 ELSE 0 END) AS converted, '
                'SUM(CASE WHEN state IN (' +
                ','.join("'%s'" % x for x in cls._lost_state()) + ') '
                'THEN 1 ELSE 0 END) AS lost, '
                'SUM(amount) AS amount, '
                'SUM(CASE WHEN state IN (' +
                ','.join("'%s'" % x for x in cls._converted_state()) + ') '
                'THEN amount ELSE 0 END) AS converted_amount '
                'FROM "' + Opportunity._table + '" '
                'GROUP BY year, month, employee, company) '
                'AS "' + cls._table + '"', [])

    def get_conversion_rate(self, name):
        if self.number:
            return float(self.converted) / self.number * 100.0
        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:
            return float(self.converted_amount) / float(self.amount) * 100.0
        else:
            return 0.0