Пример #1
0
class SaleOrder(models.Model):

    _inherit = "sale.order"

    discount_total = fields.Monetary(
        compute="_compute_discount_total",
        string="Discount Subtotal",
        currency_field="currency_id",
        store=True,
    )
    price_total_no_discount = fields.Monetary(
        compute="_compute_discount_total",
        string="Subtotal Without Discount",
        currency_field="currency_id",
        store=True,
    )

    @api.depends("order_line.discount_total",
                 "order_line.price_total_no_discount")
    def _compute_discount_total(self):
        for order in self:
            discount_total = sum(order.order_line.mapped("discount_total"))
            price_total_no_discount = sum(
                order.order_line.mapped("price_total_no_discount"))
            order.update({
                "discount_total": discount_total,
                "price_total_no_discount": price_total_no_discount,
            })
Пример #2
0
class MonetaryCustom(models.Model):
    _name = 'test_new_api.monetary_custom'
    _description = 'Monetary Related Custom'

    monetary_id = fields.Many2one('test_new_api.monetary_base')
    x_currency_id = fields.Many2one('res.currency', related='monetary_id.base_currency_id')
    x_amount = fields.Monetary(related='monetary_id.amount')
Пример #3
0
class Digest(models.Model):
    _inherit = 'digest.digest'

    kpi_account_total_revenue = fields.Boolean('Revenue')
    kpi_account_total_revenue_value = fields.Monetary(
        compute='_compute_kpi_account_total_revenue_value')

    def _compute_kpi_account_total_revenue_value(self):
        if not self.env.user.has_group('account.group_account_invoice'):
            raise AccessError(
                _("Do not have access, skip this data for user's digest email")
            )
        for record in self:
            start, end, company = record._get_kpi_compute_parameters()
            self._cr.execute(
                '''
                SELECT SUM(line.debit)
                FROM account_move_line line
                JOIN account_move move ON move.id = line.move_id
                JOIN account_journal journal ON journal.id = move.journal_id
                WHERE line.company_id = %s AND line.date >= %s AND line.date < %s
                AND journal.type = 'sale'
            ''', [company.id, start, end])
            query_res = self._cr.fetchone()
            record.kpi_account_total_revenue_value = query_res and query_res[
                0] or 0.0

    def compute_kpis_actions(self, company, user):
        res = super(Digest, self).compute_kpis_actions(company, user)
        res['kpi_account_total_revenue'] = 'account.action_move_out_invoice_type&menu_id=%s' % self.env.ref(
            'account.menu_finance').id
        return res
Пример #4
0
class MixedModel(models.Model):
    _name = 'test_new_api.mixed'
    _description = 'Test New API Mixed'

    number = fields.Float(digits=(10, 2), default=3.14)
    number2 = fields.Float(digits='New API Precision')
    date = fields.Date()
    moment = fields.Datetime()
    now = fields.Datetime(compute='_compute_now')
    lang = fields.Selection(string='Language', selection='_get_lang')
    reference = fields.Reference(string='Related Document',
        selection='_reference_models')
    comment1 = fields.Html(sanitize=False)
    comment2 = fields.Html(sanitize_attributes=True, strip_classes=False)
    comment3 = fields.Html(sanitize_attributes=True, strip_classes=True)
    comment4 = fields.Html(sanitize_attributes=True, strip_style=True)

    currency_id = fields.Many2one('res.currency', default=lambda self: self.env.ref('base.EUR'))
    amount = fields.Monetary()

    def _compute_now(self):
        # this is a non-stored computed field without dependencies
        for message in self:
            message.now = fields.Datetime.now()

    @api.model
    def _get_lang(self):
        return self.env['res.lang'].get_installed()

    @api.model
    def _reference_models(self):
        models = self.env['ir.model'].sudo().search([('state', '!=', 'manual')])
        return [(model.model, model.name)
                for model in models
                if not model.model.startswith('ir.')]
Пример #5
0
class Digest(models.Model):
    _inherit = 'digest.digest'

    kpi_all_sale_total = fields.Boolean('All Sales')
    kpi_all_sale_total_value = fields.Monetary(
        compute='_compute_kpi_sale_total_value')

    def _compute_kpi_sale_total_value(self):
        if not self.env.user.has_group(
                'sales_team.group_sale_salesman_all_leads'):
            raise AccessError(
                _("Do not have access, skip this data for user's digest email")
            )
        for record in self:
            start, end, company = record._get_kpi_compute_parameters()
            all_channels_sales = self.env['sale.report'].read_group(
                [('date', '>=', start), ('date', '<', end),
                 ('state', 'not in', ['draft', 'cancel', 'sent']),
                 ('company_id', '=', company.id)], ['price_total'],
                ['price_total'])
            record.kpi_all_sale_total_value = sum([
                channel_sale['price_total']
                for channel_sale in all_channels_sales
            ])

    def compute_kpis_actions(self, company, user):
        res = super(Digest, self).compute_kpis_actions(company, user)
        res['kpi_all_sale_total'] = 'sale.report_all_channels_sales_action&menu_id=%s' % self.env.ref(
            'sale.sale_menu_root').id
        return res
Пример #6
0
class FloatModel(models.Model):
    _name = model('float')
    _description = 'Tests: Base Import Model Float'

    value = fields.Float()
    value2 = fields.Monetary()
    currency_id = fields.Many2one('res.currency')
Пример #7
0
class Digest(models.Model):
    _inherit = 'digest.digest'

    kpi_website_sale_total = fields.Boolean('eCommerce Sales')
    kpi_website_sale_total_value = fields.Monetary(
        compute='_compute_kpi_website_sale_total_value')

    def _compute_kpi_website_sale_total_value(self):
        if not self.env.user.has_group(
                'sales_team.group_sale_salesman_all_leads'):
            raise AccessError(
                _("Do not have access, skip this data for user's digest email")
            )
        for record in self:
            start, end, company = record._get_kpi_compute_parameters()
            confirmed_website_sales = self.env['sale.order'].search([
                ('date_order', '>=', start), ('date_order', '<', end),
                ('state', 'not in', ['draft', 'cancel', 'sent']),
                ('website_id', '!=', False), ('company_id', '=', company.id)
            ])
            record.kpi_website_sale_total_value = sum(
                confirmed_website_sales.mapped('amount_total'))

    def compute_kpis_actions(self, company, user):
        res = super(Digest, self).compute_kpis_actions(company, user)
        res['kpi_website_sale_total'] = 'website.backend_dashboard&menu_id=%s' % self.env.ref(
            'website.menu_website_configuration').id
        return res
Пример #8
0
class ResConfigSettings(models.TransientModel):
    _inherit = 'res.config.settings'

    lock_confirmed_po = fields.Boolean(
        "Lock Confirmed Orders",
        default=lambda self: self.env.company.po_lock == 'lock')
    po_lock = fields.Selection(related='company_id.po_lock',
                               string="Purchase Order Modification *",
                               readonly=False)
    po_order_approval = fields.Boolean("Purchase Order Approval",
                                       default=lambda self: self.env.company.
                                       po_double_validation == 'two_step')
    po_double_validation = fields.Selection(
        related='company_id.po_double_validation',
        string="Levels of Approvals *",
        readonly=False)
    po_double_validation_amount = fields.Monetary(
        related='company_id.po_double_validation_amount',
        string="Minimum Amount",
        currency_field='company_currency_id',
        readonly=False)
    company_currency_id = fields.Many2one(
        'res.currency',
        related='company_id.currency_id',
        string="Company Currency",
        readonly=True,
        help='Utility field to express amount currency')
    default_purchase_method = fields.Selection(
        [
            ('purchase', 'Ordered quantities'),
            ('receive', 'Received quantities'),
        ],
        string="Bill Control",
        default_model="product.template",
        help="This default value is applied to any new product created. "
        "This can be changed in the product detail form.",
        default="receive")
    group_warning_purchase = fields.Boolean(
        "Purchase Warnings", implied_group='purchase.group_warning_purchase')
    module_account_3way_match = fields.Boolean(
        "3-way matching: purchases, receptions and bills")
    module_purchase_requisition = fields.Boolean("Purchase Agreements")
    module_purchase_product_matrix = fields.Boolean("Purchase Grid Entry")
    po_lead = fields.Float(related='company_id.po_lead', readonly=False)
    use_po_lead = fields.Boolean(
        string="Security Lead Time for Purchase",
        config_parameter='purchase.use_po_lead',
        help=
        "Margin of error for vendor lead times. When the system generates Purchase Orders for reordering products,they will be scheduled that many days earlier to cope with unexpected vendor delays."
    )

    @api.onchange('use_po_lead')
    def _onchange_use_po_lead(self):
        if not self.use_po_lead:
            self.po_lead = 0.0

    def set_values(self):
        super(ResConfigSettings, self).set_values()
        self.po_lock = 'lock' if self.lock_confirmed_po else 'edit'
        self.po_double_validation = 'two_step' if self.po_order_approval else 'one_step'
Пример #9
0
class Digest(models.Model):
    _inherit = 'digest.digest'

    kpi_pos_total = fields.Boolean('POS Sales')
    kpi_pos_total_value = fields.Monetary(
        compute='_compute_kpi_pos_total_value')

    def _compute_kpi_pos_total_value(self):
        if not self.env.user.has_group('point_of_sale.group_pos_user'):
            raise AccessError(
                _("Do not have access, skip this data for user's digest email")
            )
        for record in self:
            start, end, company = record._get_kpi_compute_parameters()
            record.kpi_pos_total_value = sum(self.env['pos.order'].search([
                ('date_order', '>=', start), ('date_order', '<', end),
                ('state', 'not in', ['draft', 'cancel', 'invoiced']),
                ('company_id', '=', company.id)
            ]).mapped('amount_total'))

    def compute_kpis_actions(self, company, user):
        res = super(Digest, self).compute_kpis_actions(company, user)
        res['kpi_pos_total'] = 'point_of_sale.action_pos_sale_graph&menu_id=%s' % self.env.ref(
            'point_of_sale.menu_point_root').id
        return res
Пример #10
0
class HrEmployee(models.Model):
    _inherit = 'hr.employee'

    timesheet_cost = fields.Monetary('Timesheet Cost',
                                     currency_field='currency_id',
                                     groups="hr.group_hr_user",
                                     default=0.0)
    currency_id = fields.Many2one('res.currency',
                                  related='company_id.currency_id',
                                  readonly=True)
Пример #11
0
class ComplexModel(models.Model):
    _name = model('complex')
    _description = 'Tests: Base Import Model Complex'

    f = fields.Float()
    m = fields.Monetary()
    c = fields.Char()
    currency_id = fields.Many2one('res.currency')
    d = fields.Date()
    dt = fields.Datetime()
Пример #12
0
class MonetaryOrder(models.Model):
    _name = 'test_new_api.monetary_order'
    _description = 'Sales Order'

    currency_id = fields.Many2one('res.currency')
    line_ids = fields.One2many('test_new_api.monetary_order_line', 'order_id')
    total = fields.Monetary(compute='_compute_total', store=True)

    @api.depends('line_ids.subtotal')
    def _compute_total(self):
        for record in self:
            record.total = sum(line.subtotal for line in record.line_ids)
Пример #13
0
class ResPartner(models.Model):
    """User inherits partner, so we are implicitly adding these fields to User
       This essentially reproduces the (sad) situation introduced by account.
    """
    _name = 'res.partner'
    _inherit = 'res.partner'

    currency_id = fields.Many2one('res.currency',
                                  compute='_get_company_currency',
                                  readonly=True)
    monetary = fields.Monetary(
    )  # implicitly depends on currency_id as currency_field

    def _get_company_currency(self):
        for partner in self:
            partner.currency_id = partner.sudo().company_id.currency_id
Пример #14
0
class Company(models.Model):
    _inherit = 'res.company'

    po_lead = fields.Float(string='Purchase Lead Time', required=True,
        help="Margin of error for vendor lead times. When the system "
             "generates Purchase Orders for procuring products, "
             "they will be scheduled that many days earlier "
             "to cope with unexpected vendor delays.", default=0.0)

    po_lock = fields.Selection([
        ('edit', 'Allow to edit purchase orders'),
        ('lock', 'Confirmed purchase orders are not editable')
        ], string="Purchase Order Modification", default="edit",
        help='Purchase Order Modification used when you want to purchase order editable after confirm')

    po_double_validation = fields.Selection([
        ('one_step', 'Confirm purchase orders in one step'),
        ('two_step', 'Get 2 levels of approvals to confirm a purchase order')
        ], string="Levels of Approvals", default='one_step',
        help="Provide a double validation mechanism for purchases")

    po_double_validation_amount = fields.Monetary(string='Double validation amount', default=5000,
        help="Minimum amount for which a double validation is required")
Пример #15
0
class SaleOrder(models.Model):
    _inherit = "sale.order"

    margin = fields.Monetary(compute='_product_margin', help="It gives profitability by calculating the difference between the Unit Price and the cost.", currency_field='currency_id', store=True)

    @api.depends('order_line.margin')
    def _product_margin(self):
        if not all(self._ids):
            for order in self:
                order.margin = sum(order.order_line.filtered(lambda r: r.state != 'cancel').mapped('margin'))
        else:
            self.env["sale.order.line"].flush(['margin', 'state'])
            # On batch records recomputation (e.g. at install), compute the margins
            # with a single read_group query for better performance.
            # This isn't done in an onchange environment because (part of) the data
            # may not be stored in database (new records or unsaved modifications).
            grouped_order_lines_data = self.env['sale.order.line'].read_group(
                [
                    ('order_id', 'in', self.ids),
                    ('state', '!=', 'cancel'),
                ], ['margin', 'order_id'], ['order_id'])
            mapped_data = {m['order_id'][0]: m['margin'] for m in grouped_order_lines_data}
            for order in self:
                order.margin = mapped_data.get(order.id, 0.0)
Пример #16
0
class AccountAnalyticAccount(models.Model):
    _name = 'account.analytic.account'
    _inherit = ['mail.thread']
    _description = 'Analytic Account'
    _order = 'code, name asc'

    @api.model
    def read_group(self,
                   domain,
                   fields,
                   groupby,
                   offset=0,
                   limit=None,
                   orderby=False,
                   lazy=True):
        """
            Override read_group to calculate the sum of the non-stored fields that depend on the user context
        """
        res = super(AccountAnalyticAccount, self).read_group(domain,
                                                             fields,
                                                             groupby,
                                                             offset=offset,
                                                             limit=limit,
                                                             orderby=orderby,
                                                             lazy=lazy)
        accounts = self.env['account.analytic.account']
        for line in res:
            if '__domain' in line:
                accounts = self.search(line['__domain'])
            if 'balance' in fields:
                line['balance'] = sum(accounts.mapped('balance'))
            if 'debit' in fields:
                line['debit'] = sum(accounts.mapped('debit'))
            if 'credit' in fields:
                line['credit'] = sum(accounts.mapped('credit'))
        return res

    @api.depends('line_ids.amount')
    def _compute_debit_credit_balance(self):
        Curr = self.env['res.currency']
        analytic_line_obj = self.env['account.analytic.line']
        domain = [('account_id', 'in', self.ids)]
        if self._context.get('from_date', False):
            domain.append(('date', '>=', self._context['from_date']))
        if self._context.get('to_date', False):
            domain.append(('date', '<=', self._context['to_date']))
        if self._context.get('tag_ids'):
            tag_domain = expression.OR([[('tag_ids', 'in', [tag])]
                                        for tag in self._context['tag_ids']])
            domain = expression.AND([domain, tag_domain])
        if self._context.get('company_ids'):
            domain.append(('company_id', 'in', self._context['company_ids']))

        user_currency = self.env.company.currency_id
        credit_groups = analytic_line_obj.read_group(
            domain=domain + [('amount', '>=', 0.0)],
            fields=['account_id', 'currency_id', 'amount'],
            groupby=['account_id', 'currency_id'],
            lazy=False,
        )
        data_credit = defaultdict(float)
        for l in credit_groups:
            data_credit[l['account_id'][0]] += Curr.browse(
                l['currency_id'][0])._convert(l['amount'], user_currency,
                                              self.env.company,
                                              fields.Date.today())

        debit_groups = analytic_line_obj.read_group(
            domain=domain + [('amount', '<', 0.0)],
            fields=['account_id', 'currency_id', 'amount'],
            groupby=['account_id', 'currency_id'],
            lazy=False,
        )
        data_debit = defaultdict(float)
        for l in debit_groups:
            data_debit[l['account_id'][0]] += Curr.browse(
                l['currency_id'][0])._convert(l['amount'], user_currency,
                                              self.env.company,
                                              fields.Date.today())

        for account in self:
            account.debit = abs(data_debit.get(account.id, 0.0))
            account.credit = data_credit.get(account.id, 0.0)
            account.balance = account.credit - account.debit

    name = fields.Char(string='Analytic Account',
                       index=True,
                       required=True,
                       tracking=True)
    code = fields.Char(string='Reference', index=True, tracking=True)
    active = fields.Boolean(
        'Active',
        help=
        "If the active field is set to False, it will allow you to hide the account without removing it.",
        default=True)

    group_id = fields.Many2one(
        'account.analytic.group',
        string='Group',
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]")

    line_ids = fields.One2many('account.analytic.line',
                               'account_id',
                               string="Analytic Lines")

    company_id = fields.Many2one('res.company',
                                 string='Company',
                                 default=lambda self: self.env.company)

    # use auto_join to speed up name_search call
    partner_id = fields.Many2one(
        'res.partner',
        string='Customer',
        auto_join=True,
        tracking=True,
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]")

    balance = fields.Monetary(compute='_compute_debit_credit_balance',
                              string='Balance')
    debit = fields.Monetary(compute='_compute_debit_credit_balance',
                            string='Debit')
    credit = fields.Monetary(compute='_compute_debit_credit_balance',
                             string='Credit')

    currency_id = fields.Many2one(related="company_id.currency_id",
                                  string="Currency",
                                  readonly=True)

    def name_get(self):
        res = []
        for analytic in self:
            name = analytic.name
            if analytic.code:
                name = '[' + analytic.code + '] ' + name
            if analytic.partner_id.commercial_partner_id.name:
                name = name + ' - ' + analytic.partner_id.commercial_partner_id.name
            res.append((analytic.id, name))
        return res

    @api.model
    def _name_search(self,
                     name,
                     args=None,
                     operator='ilike',
                     limit=100,
                     name_get_uid=None):
        if operator not in ('ilike', 'like', '=', '=like', '=ilike'):
            return super(AccountAnalyticAccount,
                         self)._name_search(name,
                                            args,
                                            operator,
                                            limit,
                                            name_get_uid=name_get_uid)
        args = args or []
        if operator == 'ilike' and not (name or '').strip():
            domain = []
        else:
            # `partner_id` is in auto_join and the searches using ORs with auto_join fields doesn't work
            # we have to cut the search in two searches ... https://github.com/harpiya/harpiya/issues/25175
            partner_ids = self.env['res.partner']._search(
                [('name', operator, name)],
                limit=limit,
                access_rights_uid=name_get_uid)
            domain = [
                '|', '|', ('code', operator, name), ('name', operator, name),
                ('partner_id', 'in', partner_ids)
            ]
        analytic_account_ids = self._search(expression.AND([domain, args]),
                                            limit=limit,
                                            access_rights_uid=name_get_uid)
        return models.lazy_name_get(
            self.browse(analytic_account_ids).with_user(name_get_uid))
Пример #17
0
class AccountMoveReversal(models.TransientModel):
    """
    Account move reversal wizard, it cancel an account move by reversing it.
    """
    _name = 'account.move.reversal'
    _description = 'Account Move Reversal'

    move_id = fields.Many2one('account.move',
                              string='Journal Entry',
                              domain=[('state', '=', 'posted'),
                                      ('type', 'not in', ('out_refund',
                                                          'in_refund'))])
    date = fields.Date(string='Reversal date',
                       default=fields.Date.context_today,
                       required=True)
    reason = fields.Char(string='Reason')
    refund_method = fields.Selection(
        selection=[('refund', 'Partial Refund'), ('cancel', 'Full Refund'),
                   ('modify', 'Full refund and new draft invoice')],
        string='Credit Method',
        required=True,
        help=
        'Choose how you want to credit this invoice. You cannot "modify" nor "cancel" if the invoice is already reconciled.'
    )
    journal_id = fields.Many2one(
        'account.journal',
        string='Use Specific Journal',
        help='If empty, uses the journal of the journal entry to be reversed.')

    # computed fields
    residual = fields.Monetary(compute="_compute_from_moves")
    currency_id = fields.Many2one('res.currency',
                                  compute="_compute_from_moves")
    move_type = fields.Char(compute="_compute_from_moves")

    @api.model
    def default_get(self, fields):
        res = super(AccountMoveReversal, self).default_get(fields)
        move_ids = self.env['account.move'].browse(
            self.env.context['active_ids']) if self.env.context.get(
                'active_model') == 'account.move' else self.env['account.move']
        res['refund_method'] = (len(move_ids) > 1 or move_ids.type
                                == 'entry') and 'cancel' or 'refund'
        res['residual'] = len(move_ids) == 1 and move_ids.amount_residual or 0
        res['currency_id'] = len(
            move_ids.currency_id) == 1 and move_ids.currency_id.id or False
        res['move_type'] = len(move_ids) == 1 and move_ids.type or False
        res['move_id'] = move_ids[0].id if move_ids else False
        return res

    @api.depends('move_id')
    def _compute_from_moves(self):
        move_ids = self.env['account.move'].browse(
            self.env.context['active_ids']) if self.env.context.get(
                'active_model') == 'account.move' else self.move_id
        for record in self:
            record.residual = len(
                move_ids) == 1 and move_ids.amount_residual or 0
            record.currency_id = len(
                move_ids.currency_id) == 1 and move_ids.currency_id or False
            record.move_type = len(move_ids) == 1 and move_ids.type or False

    def _prepare_default_reversal(self, move):
        return {
            'ref':
            _('Reversal of: %s, %s') %
            (move.name, self.reason) if self.reason else _('Reversal of: %s') %
            (move.name),
            'date':
            self.date or move.date,
            'invoice_date':
            move.is_invoice(include_receipts=True) and (self.date or move.date)
            or False,
            'journal_id':
            self.journal_id and self.journal_id.id or move.journal_id.id,
            'invoice_payment_term_id':
            None,
            'auto_post':
            True if self.date > fields.Date.context_today(self) else False,
            'invoice_user_id':
            move.invoice_user_id.id,
        }

    def reverse_moves(self):
        moves = self.env['account.move'].browse(
            self.env.context['active_ids']) if self.env.context.get(
                'active_model') == 'account.move' else self.move_id

        # Create default values.
        default_values_list = []
        for move in moves:
            default_values_list.append(self._prepare_default_reversal(move))

        # Handle reverse method.
        if self.refund_method == 'cancel':
            if any(
                [vals.get('auto_post', False)
                 for vals in default_values_list]):
                new_moves = moves._reverse_moves(default_values_list)
            else:
                new_moves = moves._reverse_moves(default_values_list,
                                                 cancel=True)
        elif self.refund_method == 'modify':
            moves._reverse_moves(default_values_list, cancel=True)
            moves_vals_list = []
            for move in moves.with_context(include_business_fields=True):
                moves_vals_list.append(
                    move.copy_data({
                        'date': self.date or move.date,
                    })[0])
            new_moves = self.env['account.move'].create(moves_vals_list)
        elif self.refund_method == 'refund':
            new_moves = moves._reverse_moves(default_values_list)
        else:
            return

        # Create action.
        action = {
            'name': _('Reverse Moves'),
            'type': 'ir.actions.act_window',
            'res_model': 'account.move',
        }
        if len(new_moves) == 1:
            action.update({
                'view_mode': 'form',
                'res_id': new_moves.id,
            })
        else:
            action.update({
                'view_mode': 'tree,form',
                'domain': [('id', 'in', new_moves.ids)],
            })
        return action
Пример #18
0
class Contract(models.Model):
    _name = 'hr.contract'
    _description = 'Contract'
    _inherit = ['mail.thread', 'mail.activity.mixin']

    name = fields.Char('Contract Reference', required=True)
    active = fields.Boolean(default=True)
    employee_id = fields.Many2one(
        'hr.employee',
        string='Employee',
        tracking=True,
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
    department_id = fields.Many2one(
        'hr.department',
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
        string="Department")
    job_id = fields.Many2one(
        'hr.job',
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
        string='Job Position')
    date_start = fields.Date('Start Date',
                             required=True,
                             default=fields.Date.today,
                             tracking=True,
                             help="Start date of the contract.")
    date_end = fields.Date(
        'End Date',
        tracking=True,
        help="End date of the contract (if it's a fixed-term contract).")
    trial_date_end = fields.Date(
        'End of Trial Period',
        help="End date of the trial period (if there is one).")
    resource_calendar_id = fields.Many2one(
        'resource.calendar',
        'Working Schedule',
        default=lambda self: self.env.company.resource_calendar_id.id,
        copy=False,
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
    wage = fields.Monetary('Wage',
                           required=True,
                           tracking=True,
                           help="Employee's monthly gross wage.")
    advantages = fields.Text('Advantages')
    notes = fields.Text('Notes')
    state = fields.Selection([('draft', 'New'), ('open', 'Running'),
                              ('close', 'Expired'), ('cancel', 'Cancelled')],
                             string='Status',
                             group_expand='_expand_states',
                             copy=False,
                             tracking=True,
                             help='Status of the contract',
                             default='draft')
    company_id = fields.Many2one('res.company',
                                 default=lambda self: self.env.company,
                                 required=True)
    """
        kanban_state:
            * draft + green = "Incoming" state (will be set as Open once the contract has started)
            * open + red = "Pending" state (will be set as Closed once the contract has ended)
            * red = Shows a warning on the employees kanban view
    """
    kanban_state = fields.Selection([('normal', 'Grey'), ('done', 'Green'),
                                     ('blocked', 'Red')],
                                    string='Kanban State',
                                    default='normal',
                                    tracking=True,
                                    copy=False)
    currency_id = fields.Many2one(string="Currency",
                                  related='company_id.currency_id',
                                  readonly=True)
    permit_no = fields.Char('Work Permit No',
                            related="employee_id.permit_no",
                            readonly=False)
    visa_no = fields.Char('Visa No',
                          related="employee_id.visa_no",
                          readonly=False)
    visa_expire = fields.Date('Visa Expire Date',
                              related="employee_id.visa_expire",
                              readonly=False)
    hr_responsible_id = fields.Many2one(
        'res.users',
        'HR Responsible',
        tracking=True,
        help='Person responsible for validating the employee\'s contracts.')
    calendar_mismatch = fields.Boolean(compute='_compute_calendar_mismatch')

    @api.depends('employee_id.resource_calendar_id', 'resource_calendar_id')
    def _compute_calendar_mismatch(self):
        for contract in self:
            contract.calendar_mismatch = contract.resource_calendar_id != contract.employee_id.resource_calendar_id

    def _expand_states(self, states, domain, order):
        return [key for key, val in type(self).state.selection]

    @api.onchange('employee_id')
    def _onchange_employee_id(self):
        if self.employee_id:
            self.job_id = self.employee_id.job_id
            self.department_id = self.employee_id.department_id
            self.resource_calendar_id = self.employee_id.resource_calendar_id
            self.company_id = self.employee_id.company_id

    @api.constrains('employee_id', 'state', 'kanban_state', 'date_start',
                    'date_end')
    def _check_current_contract(self):
        """ Two contracts in state [incoming | open | close] cannot overlap """
        for contract in self.filtered(lambda c: (
                c.state not in ['draft', 'cancel'] or c.state == 'draft' and c.
                kanban_state == 'done') and c.employee_id):
            domain = [
                ('id', '!=', contract.id),
                ('employee_id', '=', contract.employee_id.id),
                '|',
                ('state', 'in', ['open', 'close']),
                '&',
                ('state', '=', 'draft'),
                ('kanban_state', '=', 'done')  # replaces incoming
            ]

            if not contract.date_end:
                start_domain = []
                end_domain = [
                    '|', ('date_end', '>=', contract.date_start),
                    ('date_end', '=', False)
                ]
            else:
                start_domain = [('date_start', '<=', contract.date_end)]
                end_domain = [
                    '|', ('date_end', '>', contract.date_start),
                    ('date_end', '=', False)
                ]

            domain = expression.AND([domain, start_domain, end_domain])
            if self.search_count(domain):
                raise ValidationError(
                    _('An employee can only have one contract at the same time. (Excluding Draft and Cancelled contracts)'
                      ))

    @api.constrains('date_start', 'date_end')
    def _check_dates(self):
        if self.filtered(lambda c: c.date_end and c.date_start > c.date_end):
            raise ValidationError(
                _('Contract start date must be earlier than contract end date.'
                  ))

    @api.model
    def update_state(self):
        self.search([
            ('state', '=', 'open'),
            '|',
            '&',
            ('date_end', '<=',
             fields.Date.to_string(date.today() + relativedelta(days=7))),
            ('date_end', '>=',
             fields.Date.to_string(date.today() + relativedelta(days=1))),
            '&',
            ('visa_expire', '<=',
             fields.Date.to_string(date.today() + relativedelta(days=60))),
            ('visa_expire', '>=',
             fields.Date.to_string(date.today() + relativedelta(days=1))),
        ]).write({'kanban_state': 'blocked'})

        self.search([
            ('state', '=', 'open'),
            '|',
            ('date_end', '<=',
             fields.Date.to_string(date.today() + relativedelta(days=1))),
            ('visa_expire', '<=',
             fields.Date.to_string(date.today() + relativedelta(days=1))),
        ]).write({'state': 'close'})

        self.search([
            ('state', '=', 'draft'),
            ('kanban_state', '=', 'done'),
            ('date_start', '<=', fields.Date.to_string(date.today())),
        ]).write({'state': 'open'})

        contract_ids = self.search([('date_end', '=', False),
                                    ('state', '=', 'close'),
                                    ('employee_id', '!=', False)])
        # Ensure all closed contract followed by a new contract have a end date.
        # If closed contract has no closed date, the work entries will be generated for an unlimited period.
        for contract in contract_ids:
            next_contract = self.search(
                [('employee_id', '=', contract.employee_id.id),
                 ('state', 'not in', ['cancel', 'new']),
                 ('date_start', '>', contract.date_start)],
                order="date_start asc",
                limit=1)
            if next_contract:
                contract.date_end = next_contract.date_start - relativedelta(
                    days=1)
                continue
            next_contract = self.search(
                [('employee_id', '=', contract.employee_id.id),
                 ('date_start', '>', contract.date_start)],
                order="date_start asc",
                limit=1)
            if next_contract:
                contract.date_end = next_contract.date_start - relativedelta(
                    days=1)

        return True

    def _assign_open_contract(self):
        for contract in self:
            contract.employee_id.sudo().write({'contract_id': contract.id})

    def write(self, vals):
        res = super(Contract, self).write(vals)
        if vals.get('state') == 'open':
            self._assign_open_contract()
        if vals.get('state') == 'close':
            for contract in self.filtered(lambda c: not c.date_end):
                contract.date_end = max(date.today(), contract.date_start)

        calendar = vals.get('resource_calendar_id')
        if calendar and (self.state == 'open' or
                         (self.state == 'draft'
                          and self.kanban_state == 'done')):
            self.mapped('employee_id').write(
                {'resource_calendar_id': calendar})

        if 'state' in vals and 'kanban_state' not in vals:
            self.write({'kanban_state': 'normal'})

        return res

    @api.model
    def create(self, vals):
        contracts = super(Contract, self).create(vals)
        if vals.get('state') == 'open':
            contracts._assign_open_contract()
        open_contracts = contracts.filtered(
            lambda c: c.state == 'open' or c.state == 'draft' and c.
            kanban_state == 'done')
        # sync contract calendar -> calendar employee
        for contract in open_contracts.filtered(
                lambda c: c.employee_id and c.resource_calendar_id):
            contract.employee_id.resource_calendar_id = contract.resource_calendar_id
        return contracts

    def _track_subtype(self, init_values):
        self.ensure_one()
        if 'state' in init_values and self.state == 'open' and 'kanban_state' in init_values and self.kanban_state == 'blocked':
            return self.env.ref('hr_contract.mt_contract_pending')
        elif 'state' in init_values and self.state == 'close':
            return self.env.ref('hr_contract.mt_contract_close')
        return super(Contract, self)._track_subtype(init_values)
Пример #19
0
class CrmLead(models.Model):
    _inherit = 'crm.lead'

    sale_amount_total = fields.Monetary(
        compute='_compute_sale_data',
        string="Sum of Orders",
        help="Untaxed Total of Confirmed Orders",
        currency_field='company_currency')
    quotation_count = fields.Integer(compute='_compute_sale_data',
                                     string="Number of Quotations")
    sale_order_count = fields.Integer(compute='_compute_sale_data',
                                      string="Number of Sale Orders")
    order_ids = fields.One2many('sale.order',
                                'opportunity_id',
                                string='Orders')

    @api.depends('order_ids.state', 'order_ids.currency_id',
                 'order_ids.amount_untaxed', 'order_ids.date_order',
                 'order_ids.company_id')
    def _compute_sale_data(self):
        for lead in self:
            total = 0.0
            quotation_cnt = 0
            sale_order_cnt = 0
            company_currency = lead.company_currency or self.env.company.currency_id
            for order in lead.order_ids:
                if order.state in ('draft', 'sent'):
                    quotation_cnt += 1
                if order.state not in ('draft', 'sent', 'cancel'):
                    sale_order_cnt += 1
                    total += order.currency_id._convert(
                        order.amount_untaxed, company_currency,
                        order.company_id, order.date_order
                        or fields.Date.today())
            lead.sale_amount_total = total
            lead.quotation_count = quotation_cnt
            lead.sale_order_count = sale_order_cnt

    @api.model
    def retrieve_sales_dashboard(self):
        res = super(CrmLead, self).retrieve_sales_dashboard()
        date_today = fields.Date.from_string(fields.Date.context_today(self))

        res['invoiced'] = {
            'this_month': 0,
            'last_month': 0,
        }
        account_invoice_domain = [
            ('state', '=', 'posted'), ('invoice_user_id', '=', self.env.uid),
            ('invoice_date', '>=',
             date_today.replace(day=1) - relativedelta(months=+1)),
            ('type', 'in', ['out_invoice', 'out_refund'])
        ]

        invoice_data = self.env['account.move'].search_read(
            account_invoice_domain, ['invoice_date', 'amount_untaxed', 'type'])

        for invoice in invoice_data:
            if invoice['invoice_date']:
                invoice_date = fields.Date.from_string(invoice['invoice_date'])
                sign = 1 if invoice['type'] == 'out_invoice' else -1
                if invoice_date <= date_today and invoice_date >= date_today.replace(
                        day=1):
                    res['invoiced'][
                        'this_month'] += sign * invoice['amount_untaxed']
                elif invoice_date < date_today.replace(
                        day=1) and invoice_date >= date_today.replace(
                            day=1) - relativedelta(months=+1):
                    res['invoiced'][
                        'last_month'] += sign * invoice['amount_untaxed']

        res['invoiced']['target'] = self.env.user.target_sales_invoiced
        return res

    def action_sale_quotations_new(self):
        if not self.partner_id:
            return self.env.ref(
                "sale_crm.crm_quotation_partner_action").read()[0]
        else:
            return self.action_new_quotation()

    def action_new_quotation(self):
        action = self.env.ref("sale_crm.sale_action_quotations_new").read()[0]
        action['context'] = {
            'search_default_opportunity_id': self.id,
            'default_opportunity_id': self.id,
            'search_default_partner_id': self.partner_id.id,
            'default_partner_id': self.partner_id.id,
            'default_team_id': self.team_id.id,
            'default_campaign_id': self.campaign_id.id,
            'default_medium_id': self.medium_id.id,
            'default_origin': self.name,
            'default_source_id': self.source_id.id,
            'default_company_id': self.company_id.id or self.env.company.id,
            'default_tag_ids': self.tag_ids.ids,
        }
        return action

    def action_view_sale_quotation(self):
        action = self.env.ref(
            'sale.action_quotations_with_onboarding').read()[0]
        action['context'] = {
            'search_default_draft': 1,
            'search_default_partner_id': self.partner_id.id,
            'default_partner_id': self.partner_id.id,
            'default_opportunity_id': self.id
        }
        action['domain'] = [('opportunity_id', '=', self.id),
                            ('state', 'in', ['draft', 'sent'])]
        quotations = self.mapped('order_ids').filtered(lambda l: l.state in
                                                       ('draft', 'sent'))
        if len(quotations) == 1:
            action['views'] = [(self.env.ref('sale.view_order_form').id,
                                'form')]
            action['res_id'] = quotations.id
        return action

    def action_view_sale_order(self):
        action = self.env.ref('sale.action_orders').read()[0]
        action['context'] = {
            'search_default_partner_id': self.partner_id.id,
            'default_partner_id': self.partner_id.id,
            'default_opportunity_id': self.id,
        }
        action['domain'] = [('opportunity_id', '=', self.id),
                            ('state', 'not in', ('draft', 'sent', 'cancel'))]
        orders = self.mapped('order_ids').filtered(lambda l: l.state not in
                                                   ('draft', 'sent', 'cancel'))
        if len(orders) == 1:
            action['views'] = [(self.env.ref('sale.view_order_form').id,
                                'form')]
            action['res_id'] = orders.id
        return action
Пример #20
0
class ResPartner(models.Model):
    _name = 'res.partner'
    _inherit = 'res.partner'

    @api.depends_context('force_company')
    def _credit_debit_get(self):
        tables, where_clause, where_params = self.env[
            'account.move.line'].with_context(
                state='posted', company_id=self.env.company.id)._query_get()
        where_params = [tuple(self.ids)] + where_params
        if where_clause:
            where_clause = 'AND ' + where_clause
        self._cr.execute(
            """SELECT account_move_line.partner_id, act.type, SUM(account_move_line.amount_residual)
                      FROM """ + tables + """
                      LEFT JOIN account_account a ON (account_move_line.account_id=a.id)
                      LEFT JOIN account_account_type act ON (a.user_type_id=act.id)
                      WHERE act.type IN ('receivable','payable')
                      AND account_move_line.partner_id IN %s
                      AND account_move_line.reconciled IS NOT TRUE
                      """ + where_clause + """
                      GROUP BY account_move_line.partner_id, act.type
                      """, where_params)
        treated = self.browse()
        for pid, type, val in self._cr.fetchall():
            partner = self.browse(pid)
            if type == 'receivable':
                partner.credit = val
                if partner not in treated:
                    partner.debit = False
                    treated |= partner
            elif type == 'payable':
                partner.debit = -val
                if partner not in treated:
                    partner.credit = False
                    treated |= partner
        remaining = (self - treated)
        remaining.debit = False
        remaining.credit = False

    def _asset_difference_search(self, account_type, operator, operand):
        if operator not in ('<', '=', '>', '>=', '<='):
            return []
        if type(operand) not in (float, int):
            return []
        sign = 1
        if account_type == 'payable':
            sign = -1
        res = self._cr.execute(
            '''
            SELECT partner.id
            FROM res_partner partner
            LEFT JOIN account_move_line aml ON aml.partner_id = partner.id
            JOIN account_move move ON move.id = aml.move_id
            RIGHT JOIN account_account acc ON aml.account_id = acc.id
            WHERE acc.internal_type = %s
              AND NOT acc.deprecated AND acc.company_id = %s
              AND move.state = 'posted'
            GROUP BY partner.id
            HAVING %s * COALESCE(SUM(aml.amount_residual), 0) ''' + operator +
            ''' %s''',
            (account_type, self.env.user.company_id.id, sign, operand))
        res = self._cr.fetchall()
        if not res:
            return [('id', '=', '0')]
        return [('id', 'in', [r[0] for r in res])]

    @api.model
    def _credit_search(self, operator, operand):
        return self._asset_difference_search('receivable', operator, operand)

    @api.model
    def _debit_search(self, operator, operand):
        return self._asset_difference_search('payable', operator, operand)

    def _invoice_total(self):
        account_invoice_report = self.env['account.invoice.report']
        if not self.ids:
            return True

        user_currency_id = self.env.company.currency_id.id
        all_partners_and_children = {}
        all_partner_ids = []
        for partner in self:
            # price_total is in the company currency
            all_partners_and_children[partner] = self.with_context(
                active_test=False).search([('id', 'child_of', partner.id)]).ids
            all_partner_ids += all_partners_and_children[partner]

        # searching account.invoice.report via the ORM is comparatively expensive
        # (generates queries "id in []" forcing to build the full table).
        # In simple cases where all invoices are in the same currency than the user's company
        # access directly these elements

        # generate where clause to include multicompany rules
        where_query = account_invoice_report._where_calc([
            ('partner_id', 'in', all_partner_ids),
            ('state', 'not in', ['draft', 'cancel']),
            ('type', 'in', ('out_invoice', 'out_refund'))
        ])
        account_invoice_report._apply_ir_rules(where_query, 'read')
        from_clause, where_clause, where_clause_params = where_query.get_sql()

        # price_total is in the company currency
        query = """
                  SELECT SUM(price_subtotal) as total, partner_id
                    FROM account_invoice_report account_invoice_report
                   WHERE %s
                   GROUP BY partner_id
                """ % where_clause
        self.env.cr.execute(query, where_clause_params)
        price_totals = self.env.cr.dictfetchall()
        for partner, child_ids in all_partners_and_children.items():
            partner.total_invoiced = sum(price['total']
                                         for price in price_totals
                                         if price['partner_id'] in child_ids)

    def _compute_journal_item_count(self):
        AccountMoveLine = self.env['account.move.line']
        for partner in self:
            partner.journal_item_count = AccountMoveLine.search_count([
                ('partner_id', '=', partner.id)
            ])

    def _compute_has_unreconciled_entries(self):
        for partner in self:
            # Avoid useless work if has_unreconciled_entries is not relevant for this partner
            if not partner.active or not partner.is_company and partner.parent_id:
                partner.has_unreconciled_entries = False
                continue
            self.env.cr.execute(
                """ SELECT 1 FROM(
                        SELECT
                            p.last_time_entries_checked AS last_time_entries_checked,
                            MAX(l.write_date) AS max_date
                        FROM
                            account_move_line l
                            RIGHT JOIN account_account a ON (a.id = l.account_id)
                            RIGHT JOIN res_partner p ON (l.partner_id = p.id)
                        WHERE
                            p.id = %s
                            AND EXISTS (
                                SELECT 1
                                FROM account_move_line l
                                WHERE l.account_id = a.id
                                AND l.partner_id = p.id
                                AND l.amount_residual > 0
                            )
                            AND EXISTS (
                                SELECT 1
                                FROM account_move_line l
                                WHERE l.account_id = a.id
                                AND l.partner_id = p.id
                                AND l.amount_residual < 0
                            )
                        GROUP BY p.last_time_entries_checked
                    ) as s
                    WHERE (last_time_entries_checked IS NULL OR max_date > last_time_entries_checked)
                """, (partner.id, ))
            partner.has_unreconciled_entries = self.env.cr.rowcount == 1

    def mark_as_reconciled(self):
        self.env['account.partial.reconcile'].check_access_rights('write')
        return self.sudo().with_context(company_id=self.env.company.id).write({
            'last_time_entries_checked':
            time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
        })

    def _get_company_currency(self):
        for partner in self:
            if partner.company_id:
                partner.currency_id = partner.sudo().company_id.currency_id
            else:
                partner.currency_id = self.env.company.currency_id

    credit = fields.Monetary(compute='_credit_debit_get',
                             search=_credit_search,
                             string='Total Receivable',
                             help="Total amount this customer owes you.")
    debit = fields.Monetary(
        compute='_credit_debit_get',
        search=_debit_search,
        string='Total Payable',
        help="Total amount you have to pay to this vendor.")
    debit_limit = fields.Monetary('Payable Limit')
    total_invoiced = fields.Monetary(compute='_invoice_total',
                                     string="Total Invoiced",
                                     groups='account.group_account_invoice')
    currency_id = fields.Many2one(
        'res.currency',
        compute='_get_company_currency',
        readonly=True,
        string="Currency",
        help='Utility field to express amount currency')
    journal_item_count = fields.Integer(compute='_compute_journal_item_count',
                                        string="Journal Items",
                                        type="integer")
    property_account_payable_id = fields.Many2one(
        'account.account',
        company_dependent=True,
        string="Account Payable",
        domain=
        "[('internal_type', '=', 'payable'), ('deprecated', '=', False)]",
        help=
        "This account will be used instead of the default one as the payable account for the current partner",
        required=True)
    property_account_receivable_id = fields.Many2one(
        'account.account',
        company_dependent=True,
        string="Account Receivable",
        domain=
        "[('internal_type', '=', 'receivable'), ('deprecated', '=', False)]",
        help=
        "This account will be used instead of the default one as the receivable account for the current partner",
        required=True)
    property_account_position_id = fields.Many2one(
        'account.fiscal.position',
        company_dependent=True,
        string="Fiscal Position",
        help=
        "The fiscal position determines the taxes/accounts used for this contact."
    )
    property_payment_term_id = fields.Many2one(
        'account.payment.term',
        company_dependent=True,
        string='Customer Payment Terms',
        help=
        "This payment term will be used instead of the default one for sales orders and customer invoices"
    )
    property_supplier_payment_term_id = fields.Many2one(
        'account.payment.term',
        company_dependent=True,
        string='Vendor Payment Terms',
        help=
        "This payment term will be used instead of the default one for purchase orders and vendor bills"
    )
    ref_company_ids = fields.One2many(
        'res.company', 'partner_id', string='Companies that refers to partner')
    has_unreconciled_entries = fields.Boolean(
        compute='_compute_has_unreconciled_entries',
        help=
        "The partner has at least one unreconciled debit and credit since last time the invoices & payments matching was performed."
    )
    last_time_entries_checked = fields.Datetime(
        string='Latest Invoices & Payments Matching Date',
        readonly=True,
        copy=False,
        help=
        'Last time the invoices & payments matching was performed for this partner. '
        'It is set either if there\'s not at least an unreconciled debit and an unreconciled credit '
        'or if you click the "Done" button.')
    invoice_ids = fields.One2many('account.move',
                                  'partner_id',
                                  string='Invoices',
                                  readonly=True,
                                  copy=False)
    contract_ids = fields.One2many('account.analytic.account',
                                   'partner_id',
                                   string='Partner Contracts',
                                   readonly=True)
    bank_account_count = fields.Integer(compute='_compute_bank_count',
                                        string="Bank")
    trust = fields.Selection([('good', 'Good Debtor'),
                              ('normal', 'Normal Debtor'),
                              ('bad', 'Bad Debtor')],
                             string='Degree of trust you have in this debtor',
                             default='normal',
                             company_dependent=True)
    invoice_warn = fields.Selection(WARNING_MESSAGE,
                                    'Invoice',
                                    help=WARNING_HELP,
                                    default="no-message")
    invoice_warn_msg = fields.Text('Message for Invoice')
    # Computed fields to order the partners as suppliers/customers according to the
    # amount of their generated incoming/outgoing account moves
    supplier_rank = fields.Integer(default=0)
    customer_rank = fields.Integer(default=0)

    def _get_name_search_order_by_fields(self):
        res = super()._get_name_search_order_by_fields()
        partner_search_mode = self.env.context.get('res_partner_search_mode')
        if not partner_search_mode in ('customer', 'supplier'):
            return res
        order_by_field = 'COALESCE(res_partner.%s, 0) DESC,'
        if partner_search_mode == 'customer':
            field = 'customer_rank'
        else:
            field = 'supplier_rank'

        order_by_field = order_by_field % field
        return '%s, %s' % (res,
                           order_by_field % field) if res else order_by_field

    def _compute_bank_count(self):
        bank_data = self.env['res.partner.bank'].read_group(
            [('partner_id', 'in', self.ids)], ['partner_id'], ['partner_id'])
        mapped_data = dict([(bank['partner_id'][0], bank['partner_id_count'])
                            for bank in bank_data])
        for partner in self:
            partner.bank_account_count = mapped_data.get(partner.id, 0)

    def _find_accounting_partner(self, partner):
        ''' Find the partner for which the accounting entries will be created '''
        return partner.commercial_partner_id

    @api.model
    def _commercial_fields(self):
        return super(ResPartner, self)._commercial_fields() + \
            ['debit_limit', 'property_account_payable_id', 'property_account_receivable_id', 'property_account_position_id',
             'property_payment_term_id', 'property_supplier_payment_term_id', 'last_time_entries_checked']

    def action_view_partner_invoices(self):
        self.ensure_one()
        action = self.env.ref('account.action_move_out_invoice_type').read()[0]
        action['domain'] = [
            ('type', 'in', ('out_invoice', 'out_refund')),
            ('partner_id', 'child_of', self.id),
        ]
        action['context'] = {
            'default_type': 'out_invoice',
            'type': 'out_invoice',
            'journal_type': 'sale',
            'search_default_unpaid': 1
        }
        return action

    @api.onchange('company_id', 'parent_id')
    def _onchange_company_id(self):
        super(ResPartner, self)._onchange_company_id()
        if self.company_id:
            company = self.company_id
        else:
            company = self.env.company
        return {
            'domain': {
                'property_account_position_id':
                [('company_id', 'in', [company.id, False])]
            }
        }

    def can_edit_vat(self):
        ''' Can't edit `vat` if there is (non draft) issued invoices. '''
        can_edit_vat = super(ResPartner, self).can_edit_vat()
        if not can_edit_vat:
            return can_edit_vat
        has_invoice = self.env['account.move'].search(
            [('type', 'in', ['out_invoice', 'out_refund']),
             ('partner_id', 'child_of', self.commercial_partner_id.id),
             ('state', '=', 'posted')],
            limit=1)
        return can_edit_vat and not (bool(has_invoice))

    @api.model_create_multi
    def create(self, vals_list):
        search_partner_mode = self.env.context.get('res_partner_search_mode')
        is_customer = search_partner_mode == 'customer'
        is_supplier = search_partner_mode == 'supplier'
        if search_partner_mode:
            for vals in vals_list:
                if is_customer and 'customer_rank' not in vals:
                    vals['customer_rank'] = 1
                elif is_supplier and 'supplier_rank' not in vals:
                    vals['supplier_rank'] = 1
        return super().create(vals_list)

    def _increase_rank(self, field):
        if self.ids and field in ['customer_rank', 'supplier_rank']:
            try:
                with self.env.cr.savepoint():
                    query = sql.SQL("""
                        SELECT {field} FROM res_partner WHERE ID IN %(partner_ids)s FOR UPDATE NOWAIT;
                        UPDATE res_partner SET {field} = {field} + 1
                        WHERE id IN %(partner_ids)s
                    """).format(field=sql.Identifier(field))
                    self.env.cr.execute(query,
                                        {'partner_ids': tuple(self.ids)})
                    for partner in self:
                        self.env.cache.remove(partner, partner._fields[field])
            except DatabaseError as e:
                if e.pgcode == '55P03':
                    _logger.debug(
                        'Another transaction already locked partner rows. Cannot update partner ranks.'
                    )
                else:
                    raise e
Пример #21
0
class TaxAdjustments(models.TransientModel):
    _name = 'tax.adjustments.wizard'
    _description = 'Tax Adjustments Wizard'

    def _get_default_journal(self):
        return self.env['account.journal'].search([('type', '=', 'general')],
                                                  limit=1).id

    reason = fields.Char(string='Justification', required=True)
    journal_id = fields.Many2one('account.journal',
                                 string='Journal',
                                 required=True,
                                 default=_get_default_journal,
                                 domain=[('type', '=', 'general')])
    date = fields.Date(required=True, default=fields.Date.context_today)
    debit_account_id = fields.Many2one('account.account',
                                       string='Debit account',
                                       required=True,
                                       domain=[('deprecated', '=', False)])
    credit_account_id = fields.Many2one('account.account',
                                        string='Credit account',
                                        required=True,
                                        domain=[('deprecated', '=', False)])
    amount = fields.Monetary(currency_field='company_currency_id',
                             required=True)
    adjustment_type = fields.Selection(
        [('debit', 'Applied on debit journal item'),
         ('credit', 'Applied on credit journal item')],
        string="Adjustment Type",
        required=True)
    tax_report_line_id = fields.Many2one(
        string="Report Line",
        comodel_name='account.tax.report.line',
        required=True,
        help="The report line to make an adjustment for.")
    company_currency_id = fields.Many2one(
        'res.currency',
        readonly=True,
        default=lambda x: x.env.company.currency_id)
    country_id = fields.Many2one(string="Country",
                                 comodel_name='res.country',
                                 readonly=True,
                                 default=lambda x: x.env.company.country_id)

    def create_move(self):
        move_line_vals = []

        is_debit = self.adjustment_type == 'debit'
        sign_multiplier = (self.amount < 0 and -1 or
                           1) * (self.adjustment_type == 'credit' and -1 or 1)
        filter_lambda = (sign_multiplier < 0) and (lambda x: x.tax_negate) or (
            lambda x: not x.tax_negate)
        adjustment_tag = self.tax_report_line_id.tag_ids.filtered(
            filter_lambda)

        # Vals for the amls corresponding to the ajustment tag
        move_line_vals.append((0, 0, {
            'name':
            self.reason,
            'debit':
            is_debit and abs(self.amount) or 0,
            'credit':
            not is_debit and abs(self.amount) or 0,
            'account_id':
            is_debit and self.debit_account_id.id or self.credit_account_id.id,
            'tag_ids': [(6, False, [adjustment_tag.id])],
        }))

        # Vals for the counterpart line
        move_line_vals.append((0, 0, {
            'name':
            self.reason,
            'debit':
            not is_debit and abs(self.amount) or 0,
            'credit':
            is_debit and abs(self.amount) or 0,
            'account_id':
            is_debit and self.credit_account_id.id or self.debit_account_id.id,
        }))

        # Create the move
        vals = {
            'journal_id': self.journal_id.id,
            'date': self.date,
            'state': 'draft',
            'line_ids': move_line_vals,
        }
        move = self.env['account.move'].create(vals)
        move.post()

        # Return an action opening the created move
        action = self.env.ref(
            self.env.context.get('action', 'account.action_move_line_form'))
        result = action.read()[0]
        result['views'] = [(False, 'form')]
        result['res_id'] = move.id
        return result
class HrExpenseSheetRegisterPaymentWizard(models.TransientModel):
    _name = "hr.expense.sheet.register.payment.wizard"
    _description = "Expense Register Payment Wizard"

    @api.model
    def default_get(self, fields):
        result = super(HrExpenseSheetRegisterPaymentWizard,
                       self).default_get(fields)

        active_model = self._context.get('active_model')
        if active_model != 'hr.expense.sheet':
            raise UserError(
                _('You can only apply this action from an expense report.'))

        active_id = self._context.get('active_id')
        if 'expense_sheet_id' in fields and active_id:
            result['expense_sheet_id'] = active_id

        if 'partner_id' in fields and active_id and not result.get(
                'partner_id'):
            expense_sheet = self.env['hr.expense.sheet'].browse(active_id)
            result[
                'partner_id'] = expense_sheet.address_id.id or expense_sheet.employee_id.id and expense_sheet.employee_id.address_home_id.id
        return result

    expense_sheet_id = fields.Many2one('hr.expense.sheet',
                                       string="Expense Report",
                                       required=True)
    partner_id = fields.Many2one(
        'res.partner',
        string='Partner',
        required=True,
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
    partner_bank_account_id = fields.Many2one(
        'res.partner.bank',
        string="Recipient Bank Account",
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
    journal_id = fields.Many2one(
        'account.journal',
        string='Payment Method',
        required=True,
        domain=
        "[('type', 'in', ('bank', 'cash')), ('company_id', '=', company_id)]")
    company_id = fields.Many2one('res.company',
                                 related='expense_sheet_id.company_id',
                                 string='Company',
                                 readonly=True)
    payment_method_id = fields.Many2one('account.payment.method',
                                        string='Payment Type',
                                        required=True)
    amount = fields.Monetary(string='Payment Amount', required=True)
    currency_id = fields.Many2one(
        'res.currency',
        string='Currency',
        required=True,
        default=lambda self: self.env.company.currency_id)
    payment_date = fields.Date(string='Payment Date',
                               default=fields.Date.context_today,
                               required=True)
    communication = fields.Char(string='Memo')
    hide_payment_method = fields.Boolean(
        compute='_compute_hide_payment_method',
        help=
        "Technical field used to hide the payment method if the selected journal has only one available which is 'manual'"
    )
    show_partner_bank_account = fields.Boolean(
        compute='_compute_show_partner_bank',
        help=
        'Technical field used to know whether the field `partner_bank_account_id` needs to be displayed or not in the payments form views'
    )
    require_partner_bank_account = fields.Boolean(
        compute='_compute_show_partner_bank',
        help=
        'Technical field used to know whether the field `partner_bank_account_id` needs to be required or not in the payments form views'
    )

    @api.onchange('partner_id')
    def _onchange_partner_id(self):
        expense_sheet = self.expense_sheet_id
        if expense_sheet.employee_id.id and expense_sheet.employee_id.sudo(
        ).bank_account_id.id:
            self.partner_bank_account_id = expense_sheet.employee_id.sudo(
            ).bank_account_id.id
        elif self.partner_id and len(self.partner_id.bank_ids) > 0:
            self.partner_bank_account_id = self.partner_id.bank_ids[0]
        else:
            self.partner_bank_account_id = False

    @api.constrains('amount')
    def _check_amount(self):
        for wizard in self:
            if not wizard.amount > 0.0:
                raise ValidationError(
                    _('The payment amount must be strictly positive.'))

    @api.depends('payment_method_id')
    def _compute_show_partner_bank(self):
        """ Computes if the destination bank account must be displayed in the payment form view. By default, it
        won't be displayed but some modules might change that, depending on the payment type."""
        for payment in self:
            payment.show_partner_bank_account = payment.payment_method_id.code in self.env[
                'account.payment']._get_method_codes_using_bank_account()
            payment.require_partner_bank_account = payment.payment_method_id.code in self.env[
                'account.payment']._get_method_codes_needing_bank_account()

    @api.depends('journal_id')
    def _compute_hide_payment_method(self):
        for wizard in self:
            if not wizard.journal_id:
                wizard.hide_payment_method = True
            else:
                journal_payment_methods = wizard.journal_id.outbound_payment_method_ids
                wizard.hide_payment_method = (
                    len(journal_payment_methods) == 1
                    and journal_payment_methods[0].code == 'manual')

    @api.onchange('journal_id')
    def _onchange_journal(self):
        if self.journal_id:
            # Set default payment method (we consider the first to be the default one)
            payment_methods = self.journal_id.outbound_payment_method_ids
            self.payment_method_id = payment_methods and payment_methods[
                0] or False
            # Set payment method domain (restrict to methods enabled for the journal and to selected payment type)
            return {
                'domain': {
                    'payment_method_id': [('payment_type', '=', 'outbound'),
                                          ('id', 'in', payment_methods.ids)]
                }
            }
        return {}

    def _get_payment_vals(self):
        """ Hook for extension """
        return {
            'partner_type': 'supplier',
            'payment_type': 'outbound',
            'partner_id': self.partner_id.id,
            'partner_bank_account_id': self.partner_bank_account_id.id,
            'journal_id': self.journal_id.id,
            'company_id': self.company_id.id,
            'payment_method_id': self.payment_method_id.id,
            'amount': self.amount,
            'currency_id': self.currency_id.id,
            'payment_date': self.payment_date,
            'communication': self.communication
        }

    def expense_post_payment(self):
        self.ensure_one()
        company = self.company_id
        self = self.with_context(force_company=company.id,
                                 company_id=company.id)
        context = dict(self._context or {})
        active_ids = context.get('active_ids', [])
        expense_sheet = self.env['hr.expense.sheet'].browse(active_ids)

        # Create payment and post it
        payment = self.env['account.payment'].create(self._get_payment_vals())
        payment.post()

        # Log the payment in the chatter
        body = (_(
            "A payment of %s %s with the reference <a href='/mail/view?%s'>%s</a> related to your expense %s has been made."
        ) % (payment.amount, payment.currency_id.symbol,
             url_encode({
                 'model': 'account.payment',
                 'res_id': payment.id
             }), payment.name, expense_sheet.name))
        expense_sheet.message_post(body=body)

        # Reconcile the payment and the expense, i.e. lookup on the payable account move lines
        account_move_lines_to_reconcile = self.env['account.move.line']
        for line in payment.move_line_ids + expense_sheet.account_move_id.line_ids:
            if line.account_id.internal_type == 'payable' and not line.reconciled:
                account_move_lines_to_reconcile |= line
        account_move_lines_to_reconcile.reconcile()

        return {'type': 'ir.actions.act_window_close'}
Пример #23
0
class MonetaryBase(models.Model):
    _name = 'test_new_api.monetary_base'
    _description = 'Monetary Base'

    base_currency_id = fields.Many2one('res.currency')
    amount = fields.Monetary(currency_field='base_currency_id')
Пример #24
0
class ProjectCreateInvoice(models.TransientModel):
    _name = 'project.create.invoice'
    _description = "Create Invoice from project"

    @api.model
    def default_get(self, fields):
        result = super(ProjectCreateInvoice, self).default_get(fields)

        active_model = self._context.get('active_model')
        if active_model != 'project.project':
            raise UserError(
                _('You can only apply this action from a project.'))

        active_id = self._context.get('active_id')
        if 'project_id' in fields and active_id:
            result['project_id'] = active_id
        return result

    project_id = fields.Many2one('project.project',
                                 "Project",
                                 help="Project to make billable",
                                 required=True)
    sale_order_id = fields.Many2one('sale.order',
                                    string="Choose the Sales Order to invoice",
                                    required=True)
    amount_to_invoice = fields.Monetary(
        "Amount to invoice",
        compute='_compute_amount_to_invoice',
        currency_field='currency_id',
        help=
        "Total amount to invoice on the sales order, including all items (services, storables, expenses, ...)"
    )
    currency_id = fields.Many2one(related='sale_order_id.currency_id',
                                  readonly=True)

    @api.onchange('project_id')
    def _onchange_project_id(self):
        sale_orders = self.project_id.tasks.mapped(
            'sale_line_id.order_id').filtered(
                lambda so: so.invoice_status == 'to invoice')
        return {
            'domain': {
                'sale_order_id': [('id', 'in', sale_orders.ids)]
            },
        }

    @api.depends('sale_order_id')
    def _compute_amount_to_invoice(self):
        for wizard in self:
            amount_untaxed = 0.0
            amount_tax = 0.0
            for line in wizard.sale_order_id.order_line.filtered(
                    lambda sol: sol.invoice_status == 'to invoice'):
                amount_untaxed += line.price_reduce * line.qty_to_invoice
                amount_tax += line.price_tax
            wizard.amount_to_invoice = amount_untaxed + amount_tax

    def action_create_invoice(self):
        if not self.sale_order_id and self.sale_order_id.invoice_status != 'to invoice':
            raise UserError(
                _("The selected Sales Order should contain something to invoice."
                  ))
        action = self.env.ref(
            'sale.action_view_sale_advance_payment_inv').read()[0]
        action['context'] = {'active_ids': self.sale_order_id.ids}
        return action
Пример #25
0
class PaymentLinkWizard(models.TransientModel):
    _name = "payment.link.wizard"
    _description = "Generate Payment Link"

    @api.model
    def default_get(self, fields):
        res = super(PaymentLinkWizard, self).default_get(fields)
        res_id = self._context.get('active_id')
        res_model = self._context.get('active_model')
        res.update({'res_id': res_id, 'res_model': res_model})
        amount_field = 'amount_residual' if res_model == 'account.move' else 'amount_total'
        if res_id and res_model == 'account.move':
            record = self.env[res_model].browse(res_id)
            res.update({
                'description': record.invoice_payment_ref,
                'amount': record[amount_field],
                'currency_id': record.currency_id.id,
                'partner_id': record.partner_id.id,
                'amount_max': record[amount_field],
            })
        return res

    res_model = fields.Char('Related Document Model', required=True)
    res_id = fields.Integer('Related Document ID', required=True)
    amount = fields.Monetary(currency_field='currency_id', required=True)
    amount_max = fields.Monetary(currency_field='currency_id')
    currency_id = fields.Many2one('res.currency')
    partner_id = fields.Many2one('res.partner')
    partner_email = fields.Char(related='partner_id.email')
    link = fields.Char(string='Payment Link', compute='_compute_values')
    description = fields.Char('Payment Ref')
    access_token = fields.Char(compute='_compute_values')
    company_id = fields.Many2one('res.company', compute='_compute_company')

    @api.onchange('amount', 'description')
    def _onchange_amount(self):
        if float_compare(self.amount_max,
                         self.amount,
                         precision_rounding=self.currency_id.rounding
                         or 0.01) == -1:
            raise ValidationError(
                _("Please set an amount smaller than %s.") % (self.amount_max))
        if self.amount <= 0:
            raise ValidationError(
                _("The value of the payment amount must be positive."))

    @api.depends('amount', 'description', 'partner_id', 'currency_id')
    def _compute_values(self):
        secret = self.env['ir.config_parameter'].sudo().get_param(
            'database.secret')
        for payment_link in self:
            token_str = '%s%s%s' % (payment_link.partner_id.id,
                                    payment_link.amount,
                                    payment_link.currency_id.id)
            payment_link.access_token = hmac.new(secret.encode('utf-8'),
                                                 token_str.encode('utf-8'),
                                                 hashlib.sha256).hexdigest()
        # must be called after token generation, obvsly - the link needs an up-to-date token
        self._generate_link()

    @api.depends('res_model', 'res_id')
    def _compute_company(self):
        for link in self:
            record = self.env[link.res_model].browse(link.res_id)
            link.company_id = record.company_id if 'company_id' in record else False

    def _generate_link(self):
        base_url = self.env['ir.config_parameter'].sudo().get_param(
            'web.base.url')
        for payment_link in self:
            link = (
                '%s/website_payment/pay?reference=%s&amount=%s&currency_id=%s'
                '&partner_id=%s&access_token=%s') % (
                    base_url, urls.url_quote(payment_link.description),
                    payment_link.amount, payment_link.currency_id.id,
                    payment_link.partner_id.id, payment_link.access_token)
            if payment_link.company_id:
                link += '&company_id=%s' % payment_link.company_id.id
            payment_link.link = link

    @api.model
    def check_token(self, access_token, partner_id, amount, currency_id):
        secret = self.env['ir.config_parameter'].sudo().get_param(
            'database.secret')
        token_str = '%s%s%s' % (partner_id, amount, currency_id)
        correct_token = hmac.new(secret.encode('utf-8'),
                                 token_str.encode('utf-8'),
                                 hashlib.sha256).hexdigest()
        if consteq(ustr(access_token), correct_token):
            return True
        return False
Пример #26
0
class AccrualAccountingWizard(models.TransientModel):
    _name = 'account.accrual.accounting.wizard'
    _description = 'Create accrual entry.'

    date = fields.Date(required=True)
    company_id = fields.Many2one('res.company', required=True)
    account_type = fields.Selection([('income', 'Revenue'),
                                     ('expense', 'Expense')])
    active_move_line_ids = fields.Many2many('account.move.line')
    journal_id = fields.Many2one(
        'account.journal',
        required=True,
        readonly=False,
        domain="[('company_id', '=', company_id), ('type', '=', 'general')]",
        related="company_id.accrual_default_journal_id")
    expense_accrual_account = fields.Many2one(
        'account.account',
        readonly=False,
        domain=
        "[('company_id', '=', company_id), ('internal_type', 'not in', ('receivable', 'payable')), ('internal_group', '=', 'liability'), ('reconcile', '=', True)]",
        related="company_id.expense_accrual_account_id")
    revenue_accrual_account = fields.Many2one(
        'account.account',
        readonly=False,
        domain=
        "[('company_id', '=', company_id), ('internal_type', 'not in', ('receivable', 'payable')), ('internal_group', '=', 'asset'), ('reconcile', '=', True)]",
        related="company_id.revenue_accrual_account_id")
    percentage = fields.Float("Percentage", default=100.0)
    total_amount = fields.Monetary(compute="_compute_total_amount",
                                   currency_field='company_currency_id')
    company_currency_id = fields.Many2one('res.currency',
                                          related='company_id.currency_id')

    @api.constrains('percentage')
    def _constraint_percentage(self):
        for record in self:
            if not (0.0 < record.percentage <= 100.0):
                raise UserError(_("Percentage must be between 0 and 100"))

    @api.depends('percentage', 'active_move_line_ids')
    def _compute_total_amount(self):
        for record in self:
            record.total_amount = sum(
                record.active_move_line_ids.mapped(
                    lambda l: record.percentage * (l.debit + l.credit) / 100))

    @api.model
    def default_get(self, fields):
        if self.env.context.get(
                'active_model'
        ) != 'account.move.line' or not self.env.context.get('active_ids'):
            raise UserError(_('This can only be used on journal items'))
        rec = super(AccrualAccountingWizard, self).default_get(fields)
        active_move_line_ids = self.env['account.move.line'].browse(
            self.env.context['active_ids'])
        rec['active_move_line_ids'] = active_move_line_ids.ids

        if any(move.state != 'posted'
               for move in active_move_line_ids.mapped('move_id')):
            raise UserError(
                _('You can only change the period for posted journal items.'))
        if any(move_line.reconciled for move_line in active_move_line_ids):
            raise UserError(
                _('You can only change the period for items that are not yet reconciled.'
                  ))
        if any(line.account_id.user_type_id !=
               active_move_line_ids[0].account_id.user_type_id
               for line in active_move_line_ids):
            raise UserError(
                _('All accounts on the lines must be from the same type.'))
        if any(line.company_id != active_move_line_ids[0].company_id
               for line in active_move_line_ids):
            raise UserError(_('All lines must be from the same company.'))
        rec['company_id'] = active_move_line_ids[0].company_id.id
        account_types_allowed = self.env.ref(
            'account.data_account_type_expenses') + self.env.ref(
                'account.data_account_type_revenue') + self.env.ref(
                    'account.data_account_type_other_income')
        if active_move_line_ids[
                0].account_id.user_type_id not in account_types_allowed:
            raise UserError(
                _('You can only change the period for items in these types of accounts: '
                  ) + ", ".join(account_types_allowed.mapped('name')))
        rec['account_type'] = active_move_line_ids[
            0].account_id.user_type_id.internal_group
        return rec

    def amend_entries(self):
        # set the accrual account on the selected journal items
        accrual_account = self.revenue_accrual_account if self.account_type == 'income' else self.expense_accrual_account

        # Generate journal entries.
        move_data = {}
        for aml in self.active_move_line_ids:
            ref1 = _(
                'Accrual Adjusting Entry (%s%% recognized) for invoice: %s'
            ) % (self.percentage, aml.move_id.name)
            ref2 = _(
                'Accrual Adjusting Entry (%s%% recognized) for invoice: %s'
            ) % (100 - self.percentage, aml.move_id.name)
            move_data.setdefault(
                aml.move_id,
                (
                    [
                        # Values to create moves.
                        {
                            'date': self.date,
                            'ref': ref1,
                            'journal_id': self.journal_id.id,
                            'line_ids': [],
                        },
                        {
                            'date': aml.move_id.date,
                            'ref': ref2,
                            'journal_id': self.journal_id.id,
                            'line_ids': [],
                        },
                    ],
                    [
                        # Messages to log on the chatter.
                        (_('Accrual Adjusting Entry ({percent}% recognized) for invoice:'
                           ) +
                         ' <a href=# data-oe-model=account.move data-oe-id={id}>{name}</a>'
                         ).format(
                             percent=self.percentage,
                             id=aml.move_id.id,
                             name=aml.move_id.name,
                         ),
                        (_('Accrual Adjusting Entry ({percent}% recognized) for invoice:'
                           ) +
                         ' <a href=# data-oe-model=account.move data-oe-id={id}>{name}</a>'
                         ).format(
                             percent=100 - self.percentage,
                             id=aml.move_id.id,
                             name=aml.move_id.name,
                         ),
                        (_('Accrual Adjusting Entries ({percent}%% recognized) have been created for this invoice on {date}'
                           ) +
                         ' <a href=# data-oe-model=account.move data-oe-id=%(first_id)d>%(first_name)s</a> and <a href=# data-oe-model=account.move data-oe-id=%(second_id)d>%(second_name)s</a>'
                         ).format(
                             percent=self.percentage,
                             date=format_date(self.env, self.date),
                         ),
                    ]))

            reported_debit = aml.company_id.currency_id.round(
                (self.percentage / 100) * aml.debit)
            reported_credit = aml.company_id.currency_id.round(
                (self.percentage / 100) * aml.credit)
            if aml.currency_id:
                reported_amount_currency = aml.currency_id.round(
                    (self.percentage / 100) * aml.amount_currency)
            else:
                reported_amount_currency = 0.0

            move_data[aml.move_id][0][0]['line_ids'] += [
                (0, 0, {
                    'name': aml.name,
                    'debit': reported_debit,
                    'credit': reported_credit,
                    'amount_currency': reported_amount_currency,
                    'currency_id': aml.currency_id.id,
                    'account_id': aml.account_id.id,
                    'partner_id': aml.partner_id.id,
                }),
                (0, 0, {
                    'name': ref1,
                    'debit': reported_credit,
                    'credit': reported_debit,
                    'amount_currency': -reported_amount_currency,
                    'currency_id': aml.currency_id.id,
                    'account_id': accrual_account.id,
                    'partner_id': aml.partner_id.id,
                }),
            ]

            move_data[aml.move_id][0][1]['line_ids'] += [
                (0, 0, {
                    'name': aml.name,
                    'debit': reported_credit,
                    'credit': reported_debit,
                    'amount_currency': -reported_amount_currency,
                    'currency_id': aml.currency_id.id,
                    'account_id': aml.account_id.id,
                    'partner_id': aml.partner_id.id,
                }),
                (0, 0, {
                    'name': ref2,
                    'debit': reported_debit,
                    'credit': reported_credit,
                    'amount_currency': reported_amount_currency,
                    'currency_id': aml.currency_id.id,
                    'account_id': accrual_account.id,
                    'partner_id': aml.partner_id.id,
                }),
            ]

        move_vals = []
        log_messages = []
        for v in move_data.values():
            move_vals += v[0]
            log_messages += v[1]

        created_moves = self.env['account.move'].create(move_vals)
        created_moves.post()

        # Reconcile.
        index = 0
        for move in self.active_move_line_ids.mapped('move_id'):
            accrual_moves = created_moves[index:index + 2]

            to_reconcile = accrual_moves.mapped('line_ids').filtered(
                lambda line: line.account_id == accrual_account)
            to_reconcile.reconcile()
            move.message_post(
                body=log_messages[index // 2 + 2] % {
                    'first_id': accrual_moves[0].id,
                    'first_name': accrual_moves[0].name,
                    'second_id': accrual_moves[1].id,
                    'second_name': accrual_moves[1].name,
                })
            accrual_moves[0].message_post(body=log_messages[index // 2 + 0])
            accrual_moves[1].message_post(body=log_messages[index // 2 + 1])
            index += 2

        # open the generated entries
        action = {
            'name':
            _('Generated Entries'),
            'domain': [('id', 'in', created_moves.ids)],
            'res_model':
            'account.move',
            'view_mode':
            'tree,form',
            'type':
            'ir.actions.act_window',
            'views': [(self.env.ref('account.view_move_tree').id, 'tree'),
                      (False, 'form')],
        }
        if len(created_moves) == 1:
            action.update({'view_mode': 'form', 'res_id': created_moves.id})
        return action
Пример #27
0
class SaleOrder(models.Model):
    _inherit = 'sale.order'

    amount_delivery = fields.Monetary(compute='_compute_amount_delivery',
                                      string='Delivery Amount',
                                      help="The amount without tax.",
                                      store=True,
                                      tracking=True)

    def _compute_website_order_line(self):
        super(SaleOrder, self)._compute_website_order_line()
        for order in self:
            order.website_order_line = order.website_order_line.filtered(
                lambda l: not l.is_delivery)

    @api.depends('order_line.price_unit', 'order_line.tax_id',
                 'order_line.discount', 'order_line.product_uom_qty')
    def _compute_amount_delivery(self):
        for order in self:
            if self.env.user.has_group(
                    'account.group_show_line_subtotals_tax_excluded'):
                order.amount_delivery = sum(
                    order.order_line.filtered('is_delivery').mapped(
                        'price_subtotal'))
            else:
                order.amount_delivery = sum(
                    order.order_line.filtered('is_delivery').mapped(
                        'price_total'))

    def _check_carrier_quotation(self, force_carrier_id=None):
        self.ensure_one()
        DeliveryCarrier = self.env['delivery.carrier']

        if self.only_services:
            self.write({'carrier_id': None})
            self._remove_delivery_line()
            return True
        else:
            # attempt to use partner's preferred carrier
            if not force_carrier_id and self.partner_shipping_id.property_delivery_carrier_id:
                force_carrier_id = self.partner_shipping_id.property_delivery_carrier_id.id

            carrier = force_carrier_id and DeliveryCarrier.browse(
                force_carrier_id) or self.carrier_id
            available_carriers = self._get_delivery_methods()
            if carrier:
                if carrier not in available_carriers:
                    carrier = DeliveryCarrier
                else:
                    # set the forced carrier at the beginning of the list to be verfied first below
                    available_carriers -= carrier
                    available_carriers = carrier + available_carriers
            if force_carrier_id or not carrier or carrier not in available_carriers:
                for delivery in available_carriers:
                    verified_carrier = delivery._match_address(
                        self.partner_shipping_id)
                    if verified_carrier:
                        carrier = delivery
                        break
                self.write({'carrier_id': carrier.id})
            self._remove_delivery_line()
            if carrier:
                res = carrier.rate_shipment(self)
                if res.get('success'):
                    self.set_delivery_line(carrier, res['price'])
                    self.delivery_rating_success = True
                    self.delivery_message = res['warning_message']
                else:
                    self.set_delivery_line(carrier, 0.0)
                    self.delivery_rating_success = False
                    self.delivery_message = res['error_message']

        return bool(carrier)

    def _get_delivery_methods(self):
        address = self.partner_shipping_id
        # searching on website_published will also search for available website (_search method on computed field)
        return self.env['delivery.carrier'].sudo().search([
            ('website_published', '=', True)
        ]).available_carriers(address)

    def _cart_update(self,
                     product_id=None,
                     line_id=None,
                     add_qty=0,
                     set_qty=0,
                     **kwargs):
        """ Override to update carrier quotation if quantity changed """

        self._remove_delivery_line()

        # When you update a cart, it is not enouf to remove the "delivery cost" line
        # The carrier might also be invalid, eg: if you bought things that are too heavy
        # -> this may cause a bug if you go to the checkout screen, choose a carrier,
        #    then update your cart (the cart becomes uneditable)
        self.write({'carrier_id': False})

        values = super(SaleOrder,
                       self)._cart_update(product_id, line_id, add_qty,
                                          set_qty, **kwargs)

        return values
Пример #28
0
class StockValuationLayer(models.Model):
    """Stock Valuation Layer"""

    _name = 'stock.valuation.layer'
    _description = 'Stock Valuation Layer'
    _order = 'create_date, id'

    _rec_name = 'product_id'

    active = fields.Boolean(related='product_id.active')
    company_id = fields.Many2one('res.company',
                                 'Company',
                                 readonly=True,
                                 required=True)
    product_id = fields.Many2one('product.product',
                                 'Product',
                                 readonly=True,
                                 required=True,
                                 check_company=True)
    categ_id = fields.Many2one('product.category',
                               related='product_id.categ_id')
    product_tmpl_id = fields.Many2one('product.template',
                                      related='product_id.product_tmpl_id')
    quantity = fields.Float('Quantity',
                            digits=0,
                            help='Quantity',
                            readonly=True)
    uom_id = fields.Many2one(related='product_id.uom_id',
                             readonly=True,
                             required=True)
    currency_id = fields.Many2one('res.currency',
                                  'Currency',
                                  related='company_id.currency_id',
                                  readonly=True,
                                  required=True)
    unit_cost = fields.Monetary('Unit Value', readonly=True)
    value = fields.Monetary('Total Value', readonly=True)
    remaining_qty = fields.Float(digits=0, readonly=True)
    remaining_value = fields.Monetary('Remaining Value', readonly=True)
    description = fields.Char('Description', readonly=True)
    stock_valuation_layer_id = fields.Many2one('stock.valuation.layer',
                                               'Linked To',
                                               readonly=True,
                                               check_company=True)
    stock_valuation_layer_ids = fields.One2many('stock.valuation.layer',
                                                'stock_valuation_layer_id')
    stock_move_id = fields.Many2one('stock.move',
                                    'Stock Move',
                                    readonly=True,
                                    check_company=True,
                                    index=True)
    account_move_id = fields.Many2one('account.move',
                                      'Journal Entry',
                                      readonly=True,
                                      check_company=True)

    def init(self):
        tools.create_index(self._cr, 'stock_valuation_layer_index',
                           self._table, [
                               'product_id', 'remaining_qty', 'stock_move_id',
                               'company_id', 'create_date'
                           ])
Пример #29
0
class ProductWishlist(models.Model):
    _name = 'product.wishlist'
    _description = 'Product Wishlist'
    _sql_constraints = [
        ("product_unique_partner_id", "UNIQUE(product_id, partner_id)",
         "Duplicated wishlisted product for this partner."),
    ]

    partner_id = fields.Many2one('res.partner', string='Owner')
    product_id = fields.Many2one('product.product',
                                 string='Product',
                                 required=True)
    currency_id = fields.Many2one('res.currency',
                                  related='pricelist_id.currency_id',
                                  readonly=True)
    pricelist_id = fields.Many2one('product.pricelist',
                                   string='Pricelist',
                                   help='Pricelist when added')
    price = fields.Monetary(
        currency_field='currency_id',
        string='Price',
        help='Price of the product when it has been added in the wishlist')
    website_id = fields.Many2one('website', ondelete='cascade', required=True)
    active = fields.Boolean(default=True, required=True)

    @api.model
    def current(self):
        """Get all wishlist items that belong to current user or session,
        filter products that are unpublished."""
        if not request:
            return self

        if request.website.is_public_user():
            wish = self.sudo().search([
                ('id', 'in', request.session.get('wishlist_ids', []))
            ])
        else:
            wish = self.search([("partner_id", "=",
                                 self.env.user.partner_id.id),
                                ('website_id', '=', request.website.id)])

        return wish.filtered(
            lambda x: x.sudo().product_id.product_tmpl_id.website_published)

    @api.model
    def _add_to_wishlist(self,
                         pricelist_id,
                         currency_id,
                         website_id,
                         price,
                         product_id,
                         partner_id=False):
        wish = self.env['product.wishlist'].create({
            'partner_id': partner_id,
            'product_id': product_id,
            'currency_id': currency_id,
            'pricelist_id': pricelist_id,
            'price': price,
            'website_id': website_id,
        })
        return wish

    @api.model
    def _check_wishlist_from_session(self):
        """Assign all wishlist withtout partner from this the current session"""
        session_wishes = self.sudo().search([
            ('id', 'in', request.session.get('wishlist_ids', []))
        ])
        partner_wishes = self.sudo().search([("partner_id", "=",
                                              self.env.user.partner_id.id)])
        partner_products = partner_wishes.mapped("product_id")
        # Remove session products already present for the user
        duplicated_wishes = session_wishes.filtered(
            lambda wish: wish.product_id <= partner_products)
        session_wishes -= duplicated_wishes
        duplicated_wishes.unlink()
        # Assign the rest to the user
        session_wishes.write({"partner_id": self.env.user.partner_id.id})
        request.session.pop('wishlist_ids')

    @api.model
    def _garbage_collector(self, *args, **kwargs):
        """Remove wishlists for unexisting sessions."""
        self.with_context(active_test=False).search([
            ("create_date", "<",
             fields.Datetime.to_string(datetime.now() - timedelta(
                 weeks=kwargs.get('wishlist_week', 5)))),
            ("partner_id", "=", False),
        ]).unlink()
Пример #30
0
class AccountAnalyticLine(models.Model):
    _name = 'account.analytic.line'
    _description = 'Analytic Line'
    _order = 'date desc, id desc'

    @api.model
    def _default_user(self):
        return self.env.context.get('user_id', self.env.user.id)

    name = fields.Char('Description', required=True)
    date = fields.Date('Date',
                       required=True,
                       index=True,
                       default=fields.Date.context_today)
    amount = fields.Monetary('Amount', required=True, default=0.0)
    unit_amount = fields.Float('Quantity', default=0.0)
    product_uom_id = fields.Many2one(
        'uom.uom',
        string='Unit of Measure',
        domain="[('category_id', '=', product_uom_category_id)]")
    product_uom_category_id = fields.Many2one(
        related='product_uom_id.category_id', readonly=True)
    account_id = fields.Many2one(
        'account.analytic.account',
        'Analytic Account',
        required=True,
        ondelete='restrict',
        index=True,
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
    partner_id = fields.Many2one(
        'res.partner',
        string='Partner',
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
    user_id = fields.Many2one('res.users',
                              string='User',
                              default=_default_user)
    tag_ids = fields.Many2many(
        'account.analytic.tag',
        'account_analytic_line_tag_rel',
        'line_id',
        'tag_id',
        string='Tags',
        copy=True,
        domain=
        "['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
    company_id = fields.Many2one('res.company',
                                 string='Company',
                                 required=True,
                                 readonly=True,
                                 default=lambda self: self.env.company)
    currency_id = fields.Many2one(related="company_id.currency_id",
                                  string="Currency",
                                  readonly=True,
                                  store=True,
                                  compute_sudo=True)
    group_id = fields.Many2one('account.analytic.group',
                               related='account_id.group_id',
                               store=True,
                               readonly=True,
                               compute_sudo=True)

    @api.constrains('company_id', 'account_id')
    def _check_company_id(self):
        for line in self:
            if line.account_id.company_id and line.company_id.id != line.account_id.company_id.id:
                raise ValidationError(
                    _('The selected account belongs to another company that the one you\'re trying to create an analytic item for'
                      ))