Exemplo n.º 1
0
    def _onchange_product_id_check_availability(self):
        if not self.product_id or not self.product_uom_qty or not self.product_uom:
            self.product_packaging = False
            return {}
        if self.product_id.type == 'product':
            precision = self.env['decimal.precision'].precision_get(
                'Product Unit of Measure')
            product = self.product_id.with_context(
                warehouse=self.order_id.warehouse_id.id)
            product_qty = self.product_uom._compute_quantity(
                self.product_uom_qty, self.product_id.uom_id)
            if float_compare(product.virtual_available,
                             product_qty,
                             precision_digits=precision) == -1:
                is_available = self._check_routing()
                if not is_available:
                    message =  _('You plan to sell %s %s but you only have %s %s available in %s warehouse.') % \
                            (self.product_uom_qty, self.product_uom.name, product.virtual_available, product.uom_id.name, self.order_id.warehouse_id.name)
                    # We check if some products are available in other warehouses.
                    if float_compare(product.virtual_available,
                                     self.product_id.virtual_available,
                                     precision_digits=precision) == -1:
                        message += _('\nThere are %s %s available accross all warehouses.') % \
                                (self.product_id.virtual_available, product.uom_id.name)

                    warning_mess = {
                        'title': _('Not enough inventory!'),
                        'message': message
                    }
                    return {'warning': warning_mess}
        return {}
Exemplo n.º 2
0
 def test_Clubbed_Discount_coupon_code(self):
     if not self.sale_order_6.coupon_flag:
         self.check_all_coupon_code(self.sale_order_6, 'CD15Per',
                                    self.pricelist_2)
     self.assertTrue(self.sale_order_6.have_coupon_code != '',
                     'Coupon Code: Please enter coupon code!')
     self.assertEqual(
         float_compare(self.sale_order_6.order_line[0].discount,
                       29.90,
                       precision_digits=2), 0,
         'Discount Line: the discount of first'
         'sale order line should be 29.90!')
     self.assertEqual(
         self.sale_order_6.discount, 3969.23,
         'Sale Discount: the discount for the '
         'sale order should be 3969.23!')
     self.assertEqual(
         float_compare(self.sale_order_6.order_line[1].discount,
                       29.90,
                       precision_digits=2), 0,
         'Discount Line: the discount of first'
         'sale order line should be 29.90!')
     self.assertEqual(
         float_compare(self.sale_order_6.amount_total,
                       9305.78,
                       precision_digits=2), 0, "Total not correct")
Exemplo n.º 3
0
 def _inter_company_create_invoice(self, dest_company):
     """ create an invoice for the given company : it will copy
             the invoice lines in the new invoice.
         :param dest_company : the company of the created invoice
         :rtype dest_company : res.company record
     """
     self.ensure_one()
     # check intercompany product
     self._check_intercompany_product(dest_company)
     # if an invoice has already been genereted
     # delete it and force the same number
     inter_invoice = self.search([('auto_invoice_id', '=', self.id),
                                  ('company_id', '=', dest_company.id)])
     force_number = False
     if inter_invoice and inter_invoice.state in ['draft', 'cancel']:
         force_number = inter_invoice.move_name
         inter_invoice.move_name = False
         inter_invoice.unlink()
     # create invoice
     dest_invoice_data = self._prepare_invoice_data(dest_company)
     if force_number:
         dest_invoice_data['move_name'] = force_number
     dest_invoice = self.create(dest_invoice_data)
     # create invoice lines
     for src_line in self.invoice_line_ids:
         if dest_company.use_inter_company_products and \
                 not src_line.product_id:
             raise UserError(
                 _("The invoice line '%s' doesn't have a product. "
                   "All invoice lines should have a product for "
                   "inter-company invoices.") % src_line.name)
         dest_inv_line_data = src_line._prepare_invoice_line_data(
             dest_invoice, dest_company)
         self.env['account.invoice.line'].create(dest_inv_line_data)
     # add tax_line_ids in created invoice
     dest_invoice_line_ids = dest_invoice.invoice_line_ids
     if (any(line.invoice_line_tax_ids for line in dest_invoice_line_ids)
             and not dest_invoice.tax_line_ids):
         dest_invoice.compute_taxes()
     # Validation of account invoice
     precision = self.env['decimal.precision'].precision_get('Account')
     if (dest_company.invoice_auto_validation
             and not float_compare(self.amount_total,
                                   dest_invoice.amount_total,
                                   precision_digits=precision)):
         dest_invoice.action_invoice_open()
     else:
         # Add warning in chatter if the total amounts are different
         if float_compare(self.amount_total,
                          dest_invoice.amount_total,
                          precision_digits=precision):
             body = (_("WARNING!!!!! Failure in the inter-company invoice "
                       "creation process: the total amount of this invoice "
                       "is %s but the total amount of the invoice %s "
                       "in the company %s is %s") %
                     (dest_invoice.amount_total, self.number,
                      self.company_id.name, self.amount_total))
             dest_invoice.message_post(body=body)
     return {'dest_invoice': dest_invoice}
Exemplo n.º 4
0
 def _check_holidays(self):
     for holiday in self:
         if holiday.holiday_type != 'employee' or holiday.type != 'remove' or not holiday.employee_id or holiday.holiday_status_id.limit:
             continue
         leave_days = holiday.holiday_status_id.get_days(holiday.employee_id.id)[holiday.holiday_status_id.id]
         if float_compare(leave_days['remaining_leaves'], 0, precision_digits=2) == -1 or \
           float_compare(leave_days['virtual_remaining_leaves'], 0, precision_digits=2) == -1:
             raise ValidationError(_('The number of remaining leaves is not sufficient for this leave type.\n'
                                     'Please verify also the leaves waiting for validation.'))
Exemplo n.º 5
0
    def process(self):
        productions_to_do = self.env['mrp.production']
        productions_not_to_do = self.env['mrp.production']
        for line in self.immediate_production_line_ids:
            if line.to_immediate is True:
                productions_to_do |= line.production_id
            else:
                productions_not_to_do |= line.production_id

        for production in productions_to_do:
            error_msg = ""
            if production.product_tracking in (
                    'lot', 'serial') and not production.lot_producing_id:
                production.action_generate_serial()
            if production.product_tracking == 'serial' and float_compare(
                    production.qty_producing,
                    1,
                    precision_rounding=production.product_uom_id.rounding
            ) == 1:
                production.qty_producing = 1
            else:
                production.qty_producing = production.product_qty - production.qty_produced
            production._set_qty_producing()
            for move in production.move_raw_ids.filtered(
                    lambda m: m.state not in ['done', 'cancel']):
                rounding = move.product_uom.rounding
                for move_line in move.move_line_ids:
                    if move_line.product_uom_qty:
                        move_line.qty_done = min(
                            move_line.product_uom_qty,
                            move_line.move_id.should_consume_qty)
                    if float_compare(move.quantity_done,
                                     move.should_consume_qty,
                                     precision_rounding=rounding) >= 0:
                        break
                if float_compare(
                        move.product_uom_qty,
                        move.quantity_done,
                        precision_rounding=move.product_uom.rounding) == 1:
                    if move.has_tracking in ('serial', 'lot'):
                        error_msg += "\n  - %s" % move.product_id.display_name

            if error_msg:
                error_msg = _(
                    'You need to supply Lot/Serial Number for products:'
                ) + error_msg
                raise UserError(error_msg)

        productions_to_validate = self.env.context.get(
            'button_mark_done_production_ids')
        if productions_to_validate:
            productions_to_validate = self.env['mrp.production'].browse(
                productions_to_validate)
            productions_to_validate = productions_to_validate - productions_not_to_do
            return productions_to_validate.with_context(
                skip_immediate=True).button_mark_done()
        return True
Exemplo n.º 6
0
    def test_rounding_invalid(self):
        """ verify that invalid parameters are forbidden """
        with self.assertRaises(AssertionError):
            float_is_zero(0.01, precision_digits=3, precision_rounding=0.01)

        with self.assertRaises(AssertionError):
            float_compare(0.01, 0.02, precision_digits=3, precision_rounding=0.01)

        with self.assertRaises(AssertionError):
            float_round(0.01, precision_digits=3, precision_rounding=0.01)
Exemplo n.º 7
0
    def create_move(self, post_move=True):
        created_moves = self.env['account.move']
        prec = self.env['decimal.precision'].precision_get('Account')
        # `line.move_id` was invalidated from the cache at each iteration
        # To prevent to refetch `move_id` of all lines at each iteration just to check a UserError,
        # we use an intermediar dict which stores the information the UserError check requires.
        line_moves = {line: line.move_id for line in self}
        for line in self:
            if line_moves[line]:
                raise UserError(_('This depreciation is already linked to a journal entry! Please post or delete it.'))
            category_id = line.asset_id.category_id
            depreciation_date = self.env.context.get('depreciation_date') or line.depreciation_date or fields.Date.context_today(self)
            company_currency = line.asset_id.company_id.currency_id
            current_currency = line.asset_id.currency_id
            amount = current_currency.with_context(date=depreciation_date).compute(line.amount, company_currency)
            asset_name = line.asset_id.name + ' (%s/%s)' % (line.sequence, len(line.asset_id.depreciation_line_ids))
            move_line_1 = {
                'name': asset_name,
                'account_id': category_id.account_depreciation_id.id,
                'debit': 0.0 if float_compare(amount, 0.0, precision_digits=prec) > 0 else -amount,
                'credit': amount if float_compare(amount, 0.0, precision_digits=prec) > 0 else 0.0,
                'journal_id': category_id.journal_id.id,
                'partner_id': line.asset_id.partner_id.id,
                'analytic_account_id': category_id.account_analytic_id.id if category_id.type == 'sale' else False,
                'currency_id': company_currency != current_currency and current_currency.id or False,
                'amount_currency': company_currency != current_currency and - 1.0 * line.amount or 0.0,
            }
            move_line_2 = {
                'name': asset_name,
                'account_id': category_id.account_depreciation_expense_id.id,
                'credit': 0.0 if float_compare(amount, 0.0, precision_digits=prec) > 0 else -amount,
                'debit': amount if float_compare(amount, 0.0, precision_digits=prec) > 0 else 0.0,
                'journal_id': category_id.journal_id.id,
                'partner_id': line.asset_id.partner_id.id,
                'analytic_account_id': category_id.account_analytic_id.id if category_id.type == 'purchase' else False,
                'currency_id': company_currency != current_currency and current_currency.id or False,
                'amount_currency': company_currency != current_currency and line.amount or 0.0,
            }
            move_vals = {
                'ref': line.asset_id.code,
                'date': depreciation_date or False,
                'journal_id': category_id.journal_id.id,
                'asset_id': line.asset_id.id,
                'line_ids': [(0, 0, move_line_1), (0, 0, move_line_2)],
            }
            move = self.env['account.move'].create(move_vals)
            line.write({'move_id': move.id, 'move_check': True})
            line_moves[line] = move
            created_moves |= move

        if post_move and created_moves:
            created_moves.filtered(lambda m: any(m.asset_depreciation_ids.mapped('asset_id.category_id.open_asset'))).post()
        return [x.id for x in created_moves]
Exemplo n.º 8
0
    def _compute_qty_to_order(self):
        for orderpoint in self:
            if not orderpoint.product_id or not orderpoint.location_id:
                orderpoint.qty_to_order = False
                continue
            qty_to_order = 0.0
            rounding = orderpoint.product_uom.rounding
            if float_compare(orderpoint.qty_forecast, orderpoint.product_min_qty, precision_rounding=rounding) < 0:
                qty_to_order = max(orderpoint.product_min_qty, orderpoint.product_max_qty) - orderpoint.qty_forecast

                remainder = orderpoint.qty_multiple > 0 and qty_to_order % orderpoint.qty_multiple or 0.0
                if float_compare(remainder, 0.0, precision_rounding=rounding) > 0:
                    qty_to_order += orderpoint.qty_multiple - remainder
            orderpoint.qty_to_order = qty_to_order
Exemplo n.º 9
0
    def create_move(self, post_move=True):
        created_moves = self.env['account.move']
        for line in self:
            category_id = line.asset_id.category_id
            depreciation_date = self.env.context.get('depreciation_date') or line.depreciation_date or fields.Date.context_today(self)
            company_currency = line.asset_id.company_id.currency_id
            current_currency = line.asset_id.currency_id
            amount = current_currency.compute(line.amount, company_currency)
            sign = (category_id.journal_id.type == 'purchase' or category_id.journal_id.type == 'sale' and 1) or -1
            asset_name = line.asset_id.name + ' (%s/%s)' % (line.sequence, len(line.asset_id.depreciation_line_ids))
            prec = self.env['decimal.precision'].precision_get('Account')
            
            move_line_1 = {
                'name': asset_name,
                'account_id': category_id.account_depreciation_id.id,
                'debit': 0.0 if float_compare(amount, 0.0, precision_digits=prec) > 0 else -amount,
                'credit': amount if float_compare(amount, 0.0, precision_digits=prec) > 0 else 0.0,
                'journal_id': category_id.journal_id.id,
                'partner_id': line.asset_id.partner_id.id,
                'analytic_account_id': category_id.account_analytic_id.id if category_id.type == 'sale' else False,
                'currency_id': company_currency != current_currency and current_currency.id or False,
                'amount_currency': company_currency != current_currency and - sign * line.amount or 0.0,
            }
            move_line_2 = {
                'name': asset_name,
                'account_id': category_id.account_depreciation_expense_id.id,
                'credit': 0.0 if float_compare(amount, 0.0, precision_digits=prec) > 0 else -amount,
                'debit': amount if float_compare(amount, 0.0, precision_digits=prec) > 0 else 0.0,
                'journal_id': category_id.journal_id.id,
                'partner_id': line.asset_id.partner_id.id,
                'product_id': category_id.product_id.id if category_id.product_id else False,
                'analytic_account_id': category_id.account_analytic_id.id if category_id.type == 'purchase' else False,
                'currency_id': company_currency != current_currency and current_currency.id or False,
                'amount_currency': company_currency != current_currency and sign * line.amount or 0.0,
            }
            move_vals = {
                'ref': line.asset_id.code,
                'date': depreciation_date or False,
                'journal_id': category_id.journal_id.id,
                'line_ids': [(0, 0, move_line_1), (0, 0, move_line_2)],
            }
            move = self.env['account.move'].create(move_vals)
            line.write({'move_id': move.id, 'move_check': True})
            created_moves |= move

        if post_move and created_moves:
            created_moves.filtered(lambda m: any(m.asset_depreciation_ids.mapped('asset_id.category_id.open_asset'))).post()
        return [x.id for x in created_moves]
Exemplo n.º 10
0
 def do_produce(self):
     # Nothing to do for lots since values are created using default data (stock.move.lots)
     quantity = self.product_qty
     if float_compare(quantity, 0, precision_rounding=self.product_uom_id.rounding) <= 0:
         raise UserError(_("The production order for '%s' has no quantity specified") % self.product_id.display_name)
     for move in self.production_id.move_raw_ids:
         # TODO currently not possible to guess if the user updated quantity by hand or automatically by the produce wizard.
         if move.product_id.tracking == 'none' and move.state not in ('done', 'cancel') and move.unit_factor:
             rounding = move.product_uom.rounding
             if self.product_id.tracking != 'none':
                 qty_to_add = float_round(quantity * move.unit_factor, precision_rounding=rounding)
                 move._generate_consumed_move_line(qty_to_add, self.lot_id)
             elif len(move._get_move_lines()) < 2:
                 move.quantity_done += float_round(quantity * move.unit_factor, precision_rounding=rounding)
             else:
                 move._set_quantity_done(quantity * move.unit_factor)
     for move in self.production_id.move_finished_ids:
         if move.product_id.tracking == 'none' and move.state not in ('done', 'cancel'):
             rounding = move.product_uom.rounding
             if move.product_id.id == self.production_id.product_id.id:
                 move.quantity_done += float_round(quantity, precision_rounding=rounding)
             elif move.unit_factor:
                 # byproducts handling
                 move.quantity_done += float_round(quantity * move.unit_factor, precision_rounding=rounding)
     self.check_finished_move_lots()
     if self.production_id.state == 'confirmed':
         self.production_id.write({
             'state': 'progress',
             'date_start': datetime.now(),
         })
     return {'type': 'ir.actions.act_window_close'}
Exemplo n.º 11
0
    def _check_sum(self):
        """ Check if each cost line its valuation lines sum to the correct amount
        and if the overall total amount is correct also """
        prec_digits = self.env['decimal.precision'].precision_get('Account')
        for landed_cost in self:
            total_amount = sum(landed_cost.valuation_adjustment_lines.mapped('additional_landed_cost'))
            if not tools.float_compare(total_amount, landed_cost.amount_total, precision_digits=prec_digits) == 0:
                return False

            val_to_cost_lines = defaultdict(lambda: 0.0)
            for val_line in landed_cost.valuation_adjustment_lines:
                val_to_cost_lines[val_line.cost_line_id] += val_line.additional_landed_cost
            if any(tools.float_compare(cost_line.price_unit, val_amount, precision_digits=prec_digits) != 0
                   for cost_line, val_amount in val_to_cost_lines.items()):
                return False
        return True
Exemplo n.º 12
0
    def _confirm_invoice(self):
        """ Check tx state, confirm and pay potential invoice """
        self.ensure_one()
        # check tx state, confirm the potential SO
        if self.account_invoice_id.state != 'open':
            _logger.warning('<%s> transaction STATE INCORRECT for invoice %s (ID %s, state %s)', self.acquirer_id.provider, self.account_invoice_id.number, self.account_invoice_id.id, self.account_invoice_id.state)
            return 'pay_invoice_invalid_doc_state'
        if not float_compare(self.amount, self.account_invoice_id.amount_total, 2) == 0:
            _logger.warning(
                '<%s> transaction AMOUNT MISMATCH for invoice %s (ID %s): expected %r, got %r',
                self.acquirer_id.provider, self.account_invoice_id.number, self.account_invoice_id.id,
                self.account_invoice_id.amount_total, self.amount,
            )
            self.account_invoice_id.message_post(
                subject=_("Amount Mismatch (%s)") % self.acquirer_id.provider,
                body=_("The invoice was not confirmed despite response from the acquirer (%s): invoice amount is %r but acquirer replied with %r.") % (
                    self.acquirer_id.provider,
                    self.account_invoice_id.amount_total,
                    self.amount,
                )
            )
            return 'pay_invoice_tx_amount'

        if self.state == 'authorized' and self.acquirer_id.capture_manually:
            _logger.info('<%s> transaction authorized, nothing to do with invoice %s (ID %s)', self.acquirer_id.provider, self.account_invoice_id.number, self.account_invoice_id.id)
        elif self.state == 'done':
            _logger.info('<%s> transaction completed, paying invoice %s (ID %s)', self.acquirer_id.provider, self.account_invoice_id.number, self.account_invoice_id.id)
            self._pay_invoice()
        else:
            _logger.warning('<%s> transaction MISMATCH for invoice %s (ID %s)', self.acquirer_id.provider, self.account_invoice_id.number, self.account_invoice_id.id)
            return 'pay_invoice_tx_state'
        return True
Exemplo n.º 13
0
    def _select_seller(self, partner_id=False, quantity=0.0, date=None, uom_id=False, params=False):
        self.ensure_one()
        if date is None:
            date = fields.Date.context_today(self)
        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')

        res = self.env['product.supplierinfo']
        sellers = self._prepare_sellers(params)
        sellers = sellers.filtered(lambda s: not s.company_id or s.company_id.id == self.env.company.id)
        for seller in sellers:
            # Set quantity in UoM of seller
            quantity_uom_seller = quantity
            if quantity_uom_seller and uom_id and uom_id != seller.product_uom:
                quantity_uom_seller = uom_id._compute_quantity(quantity_uom_seller, seller.product_uom)

            if seller.date_start and seller.date_start > date:
                continue
            if seller.date_end and seller.date_end < date:
                continue
            if partner_id and seller.name not in [partner_id, partner_id.parent_id]:
                continue
            if quantity is not None and float_compare(quantity_uom_seller, seller.min_qty, precision_digits=precision) == -1:
                continue
            if seller.product_id and seller.product_id != self:
                continue
            if not res or res.name == seller.name:
                res |= seller
        return res.sorted('price')[:1]
Exemplo n.º 14
0
 def action_validate(self):
     self.ensure_one()
     precision = self.env['decimal.precision'].precision_get(
         'Product Unit of Measure')
     available_qty = self.env['stock.quant']._get_available_quantity(
         self.product_id, self.location_id, self.lot_id, strict=True)
     if float_compare(available_qty,
                      self.product_qty,
                      precision_digits=precision) >= 0:
         return self.action_unbuild()
     else:
         return {
             'name':
             _('Insufficient Quantity'),
             'view_type':
             'form',
             'view_mode':
             'form',
             'res_model':
             'stock.warn.insufficient.qty.unbuild',
             'view_id':
             self.env.ref(
                 'mrp.stock_warn_insufficient_qty_unbuild_form_view').id,
             'type':
             'ir.actions.act_window',
             'context': {
                 'default_product_id': self.product_id.id,
                 'default_location_id': self.location_id.id,
                 'default_unbuild_id': self.id
             },
             'target':
             'new'
         }
Exemplo n.º 15
0
    def _onchange_quantity_context(self):
        if self.product_id:
            self.product_uom_id = self.product_id.uom_id
        if self.product_id and self.location_id and self.product_id.uom_id.category_id == self.product_uom_id.category_id:  # TDE FIXME: last part added because crash
            theoretical_qty = self.product_id.get_theoretical_quantity(
                self.product_id.id,
                self.location_id.id,
                lot_id=self.prod_lot_id.id,
                package_id=self.package_id.id,
                owner_id=self.partner_id.id,
                to_uom=self.product_uom_id.id,
            )
        else:
            theoretical_qty = 0
        # Sanity check on the lot.
        if self.prod_lot_id:
            if self.product_id.tracking == 'none' or self.product_id != self.prod_lot_id.product_id:
                self.prod_lot_id = False

        if self.prod_lot_id and self.product_id.tracking == 'serial':
            # We force `product_qty` to 1 for SN tracked product because it's
            # the only relevant value aside 0 for this kind of product.
            self.product_qty = 1
        elif self.product_id and float_compare(self.product_qty, self.theoretical_qty, precision_rounding=self.product_uom_id.rounding) == 0:
            # We update `product_qty` only if it equals to `theoretical_qty` to
            # avoid to reset quantity when user manually set it.
            self.product_qty = theoretical_qty
        self.theoretical_qty = theoretical_qty
Exemplo n.º 16
0
 def action_validate(self):
     if not self.exists():
         return
     self.ensure_one()
     if not self.user_has_groups('stock.group_stock_manager'):
         raise UserError(_("Only a stock manager can validate an inventory adjustment."))
     if self.state != 'confirm':
         raise UserError(_(
             "You can't validate the inventory '%s', maybe this inventory "
             "has been already validated or isn't ready.", self.name))
     inventory_lines = self.line_ids.filtered(lambda l: l.product_id.tracking in ['lot', 'serial'] and not l.prod_lot_id and l.theoretical_qty != l.product_qty)
     lines = self.line_ids.filtered(lambda l: float_compare(l.product_qty, 1, precision_rounding=l.product_uom_id.rounding) > 0 and l.product_id.tracking == 'serial' and l.prod_lot_id)
     if inventory_lines and not lines:
         wiz_lines = [(0, 0, {'product_id': product.id, 'tracking': product.tracking}) for product in inventory_lines.mapped('product_id')]
         wiz = self.env['stock.track.confirmation'].create({'inventory_id': self.id, 'tracking_line_ids': wiz_lines})
         return {
             'name': _('Tracked Products in Inventory Adjustment'),
             'type': 'ir.actions.act_window',
             'view_mode': 'form',
             'views': [(False, 'form')],
             'res_model': 'stock.track.confirmation',
             'target': 'new',
             'res_id': wiz.id,
         }
     self._action_done()
     self.line_ids._check_company()
     self._check_company()
     return True
Exemplo n.º 17
0
    def _select_seller(self, partner_id=False, quantity=0.0, date=None, uom_id=False):
        self.ensure_one()
        if date is None:
            date = fields.Date.context_today(self)
        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')

        res = self.env['product.supplierinfo']
        for seller in self.seller_ids:
            # Set quantity in UoM of seller
            quantity_uom_seller = quantity
            if quantity_uom_seller and uom_id and uom_id != seller.product_uom:
                quantity_uom_seller = uom_id._compute_quantity(quantity_uom_seller, seller.product_uom)

            if seller.date_start and seller.date_start > date:
                continue
            if seller.date_end and seller.date_end < date:
                continue
            if partner_id and seller.name not in [partner_id, partner_id.parent_id]:
                continue
            if float_compare(quantity_uom_seller, seller.min_qty, precision_digits=precision) == -1:
                continue
            if seller.product_id and seller.product_id != self:
                continue

            res |= seller
            break
        return res
Exemplo n.º 18
0
 def action_validate(self):
     self.ensure_one()
     if self.product_id.type != 'product':
         return self.do_scrap()
     precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
     available_qty = sum(self.env['stock.quant']._gather(self.product_id,
                                                         self.location_id,
                                                         self.lot_id,
                                                         self.package_id,
                                                         self.owner_id,
                                                         strict=True).mapped('quantity'))
     scrap_qty = self.product_uom_id._compute_quantity(self.scrap_qty, self.product_id.uom_id)
     if float_compare(available_qty, scrap_qty, precision_digits=precision) >= 0:
         return self.do_scrap()
     else:
         return {
             'name': _('Insufficient Quantity'),
             'view_type': 'form',
             'view_mode': 'form',
             'res_model': 'stock.warn.insufficient.qty.scrap',
             'view_id': self.env.ref('stock.stock_warn_insufficient_qty_scrap_form_view').id,
             'type': 'ir.actions.act_window',
             'context': {
                 'default_product_id': self.product_id.id,
                 'default_location_id': self.location_id.id,
                 'default_scrap_id': self.id
             },
             'target': 'new'
         }
Exemplo n.º 19
0
 def _compute_consumed_less_than_planned(self):
     for order in self:
         order.consumed_less_than_planned = any(
             order.move_raw_ids.filtered(lambda move: float_compare(
                 move.quantity_done,
                 move.product_uom_qty,
                 precision_rounding=move.product_uom.rounding) == -1))
Exemplo n.º 20
0
 def _check_package(self):
     default_uom = self.product_id.uom_id
     pack = self.product_packaging
     qty = self.product_uom_qty
     q = default_uom._compute_quantity(pack.qty, self.product_uom)
     # We do not use the modulo operator to check if qty is a mltiple of q. Indeed the quantity
     # per package might be a float, leading to incorrect results. For example:
     # 8 % 1.6 = 1.5999999999999996
     # 5.4 % 1.8 = 2.220446049250313e-16
     if (qty and q
             and float_compare(qty / q,
                               float_round(qty / q, precision_rounding=1.0),
                               precision_rounding=0.001) != 0):
         newqty = qty - (qty % q) + q
         return {
             'warning': {
                 'title':
                 _('Warning'),
                 'message':
                 _("This product is packaged by %(pack_size).2f %(pack_name)s. You should sell %(quantity).2f %(unit)s.",
                   pack_size=pack.qty,
                   pack_name=default_uom.name,
                   quantity=newqty,
                   unit=self.product_uom.name),
             },
         }
     return {}
Exemplo n.º 21
0
 def action_validate(self):
     self.ensure_one()
     if self.filtered(lambda repair: any(op.product_uom_qty < 0 for op in repair.operations)):
         raise UserError(_("You can not enter negative quantities."))
     if self.product_id.type == 'consu':
         return self.action_repair_confirm()
     precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
     available_qty_owner = self.env['stock.quant']._get_available_quantity(self.product_id, self.location_id, self.lot_id, owner_id=self.partner_id, strict=True)
     available_qty_noown = self.env['stock.quant']._get_available_quantity(self.product_id, self.location_id, self.lot_id, strict=True)
     repair_qty = self.product_uom._compute_quantity(self.product_qty, self.product_id.uom_id)
     for available_qty in [available_qty_owner, available_qty_noown]:
         if float_compare(available_qty, repair_qty, precision_digits=precision) >= 0:
             return self.action_repair_confirm()
     else:
         return {
             'name': self.product_id.display_name + _(': Insufficient Quantity To Repair'),
             'view_mode': 'form',
             'res_model': 'stock.warn.insufficient.qty.repair',
             'view_id': self.env.ref('repair.stock_warn_insufficient_qty_repair_form_view').id,
             'type': 'ir.actions.act_window',
             'context': {
                 'default_product_id': self.product_id.id,
                 'default_location_id': self.location_id.id,
                 'default_repair_id': self.id,
                 'default_quantity': repair_qty,
                 'default_product_uom_name': self.product_id.uom_name
             },
             'target': 'new'
         }
Exemplo n.º 22
0
 def _compute_invoice_status(self):
     """
     Compute the invoice status of a SO line. Possible statuses:
     - no: if the SO is not in status 'sale' or 'done', we consider that there is nothing to
       invoice. This is also hte default value if the conditions of no other status is met.
     - to invoice: we refer to the quantity to invoice of the line. Refer to method
       `_get_to_invoice_qty()` for more information on how this quantity is calculated.
     - upselling: this is possible only for a product invoiced on ordered quantities for which
       we delivered more than expected. The could arise if, for example, a project took more
       time than expected but we decided not to invoice the extra cost to the client. This
       occurs onyl in state 'sale', so that when a SO is set to done, the upselling opportunity
       is removed from the list.
     - invoiced: the quantity invoiced is larger or equal to the quantity ordered.
     """
     precision = self.env['decimal.precision'].precision_get(
         'Product Unit of Measure')
     for line in self:
         if not line.order_id.invoice_policy:
             if line.state not in ('sale', 'done'):
                 line.invoice_status = 'no'
             elif not float_is_zero(line.qty_to_invoice,
                                    precision_digits=precision):
                 line.invoice_status = 'to invoice'
             elif line.state == 'sale' and line.product_id.invoice_policy == 'order' and\
                     float_compare(line.qty_delivered, line.product_uom_qty, precision_digits=precision) == 1:
                 line.invoice_status = 'upselling'
             elif float_compare(line.qty_invoiced,
                                line.product_uom_qty,
                                precision_digits=precision) >= 0:
                 line.invoice_status = 'invoiced'
             else:
                 line.invoice_status = 'no'
         else:
             if line.state not in ('sale', 'done'):
                 line.invoice_status = 'no'
             elif not float_is_zero(line.qty_to_invoice,
                                    precision_digits=precision):
                 line.invoice_status = 'to invoice'
             elif line.state == 'sale' and line.order_id.invoice_policy == 'order' and\
                     float_compare(line.qty_delivered, line.product_uom_qty, precision_digits=precision) == 1:
                 line.invoice_status = 'upselling'
             elif float_compare(line.qty_invoiced,
                                line.product_uom_qty,
                                precision_digits=precision) >= 0:
                 line.invoice_status = 'invoiced'
             else:
                 line.invoice_status = 'no'
Exemplo n.º 23
0
    def _action_launch_procurement_rule(self):
        """
        Launch procurement group run method with required/custom fields genrated by a
        sale order line. procurement group will launch '_run_move', '_run_buy' or '_run_manufacture'
        depending on the sale order line product rule.
        """
        precision = self.env['decimal.precision'].precision_get(
            'Product Unit of Measure')
        errors = []
        for line in self:
            if line.state != 'sale' or not line.product_id.type in ('consu',
                                                                    'product'):
                continue
            qty = 0.0
            for move in line.move_ids.filtered(lambda r: r.state != 'cancel'):
                qty += move.product_qty
            if float_compare(qty,
                             line.product_uom_qty,
                             precision_digits=precision) >= 0:
                continue

            group_id = line.order_id.procurement_group_id
            if not group_id:
                group_id = self.env['procurement.group'].create({
                    'name':
                    line.order_id.name,
                    'move_type':
                    line.order_id.picking_policy,
                    'sale_id':
                    line.order_id.id,
                    'partner_id':
                    line.order_id.partner_shipping_id.id,
                })
                line.order_id.procurement_group_id = group_id
            else:
                # In case the procurement group is already created and the order was
                # cancelled, we need to update certain values of the group.
                updated_vals = {}
                if group_id.partner_id != line.order_id.partner_shipping_id:
                    updated_vals.update(
                        {'partner_id': line.order_id.partner_shipping_id.id})
                if group_id.move_type != line.order_id.picking_policy:
                    updated_vals.update(
                        {'move_type': line.order_id.picking_policy})
                if updated_vals:
                    group_id.write(updated_vals)

            values = line._prepare_procurement_values(group_id=group_id)
            product_qty = line.product_uom_qty - qty
            try:
                self.env['procurement.group'].run(
                    line.product_id, product_qty, line.product_uom,
                    line.order_id.partner_shipping_id.property_stock_customer,
                    line.name, line.order_id.name, values)
            except UserError as error:
                errors.append(error.name)
        if errors:
            raise UserError('\n'.join(errors))
        return True
Exemplo n.º 24
0
    def _confirm_so(self):
        """ Check tx state, confirm the potential SO """
        self.ensure_one()
        if self.sale_order_id.state not in ['draft', 'sent', 'sale']:
            _logger.warning(
                '<%s> transaction STATE INCORRECT for order %s (ID %s, state %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id, self.sale_order_id.state)
            return 'pay_sale_invalid_doc_state'
        if not float_compare(self.amount, self.sale_order_id.amount_total,
                             2) == 0:
            _logger.warning(
                '<%s> transaction AMOUNT MISMATCH for order %s (ID %s): expected %r, got %r',
                self.acquirer_id.provider,
                self.sale_order_id.name,
                self.sale_order_id.id,
                self.sale_order_id.amount_total,
                self.amount,
            )
            self.sale_order_id.message_post(
                subject=_("Amount Mismatch (%s)") % self.acquirer_id.provider,
                body=
                _("The sale order was not confirmed despite response from the acquirer (%s): SO amount is %r but acquirer replied with %r."
                  ) % (
                      self.acquirer_id.provider,
                      self.sale_order_id.amount_total,
                      self.amount,
                  ))
            return 'pay_sale_tx_amount'

        if self.state == 'authorized' and self.acquirer_id.capture_manually:
            _logger.info(
                '<%s> transaction authorized, auto-confirming order %s (ID %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id)
            if self.sale_order_id.state in ('draft', 'sent'):
                self.sale_order_id.with_context(
                    send_email=True).action_confirm()
        elif self.state == 'done':
            _logger.info(
                '<%s> transaction completed, auto-confirming order %s (ID %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id)
            if self.sale_order_id.state in ('draft', 'sent'):
                self.sale_order_id.with_context(
                    send_email=True).action_confirm()
        elif self.state not in ['cancel', 'error'
                                ] and self.sale_order_id.state == 'draft':
            _logger.info(
                '<%s> transaction pending/to confirm manually, sending quote email for order %s (ID %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id)
            self.sale_order_id.force_quotation_send()
        else:
            _logger.warning('<%s> transaction MISMATCH for order %s (ID %s)',
                            self.acquirer_id.provider, self.sale_order_id.name,
                            self.sale_order_id.id)
            return 'pay_sale_tx_state'
        return True
Exemplo n.º 25
0
 def _compute_is_produced(self):
     self.is_produced = False
     for order in self.filtered(
             lambda p: p.production_id and p.production_id.product_uom_id):
         rounding = order.production_id.product_uom_id.rounding
         order.is_produced = float_compare(order.qty_produced,
                                           order.production_id.product_qty,
                                           precision_rounding=rounding) >= 0
Exemplo n.º 26
0
 def _reservation_is_updatable(self, quantity, reserved_quant):
     self.ensure_one()
     if self.produce_line_ids.lot_id:
         ml_remaining_qty = self.qty_done - self.product_uom_qty
         ml_remaining_qty = self.product_uom_id._compute_quantity(ml_remaining_qty, self.product_id.uom_id, rounding_method="HALF-UP")
         if float_compare(ml_remaining_qty, quantity, precision_rounding=self.product_id.uom_id.rounding) < 0:
             return False
     return super(StockMoveLine, self)._reservation_is_updatable(quantity, reserved_quant)
Exemplo n.º 27
0
    def _run_pull(self, procurements):
        moves_values_by_company = defaultdict(list)
        mtso_products_by_locations = defaultdict(list)

        # To handle the `mts_else_mto` procure method, we do a preliminary loop to
        # isolate the products we would need to read the forecasted quantity,
        # in order to to batch the read. We also make a sanitary check on the
        # `location_src_id` field.
        for procurement, rule in procurements:
            if not rule.location_src_id:
                msg = _('No source location defined on stock rule: %s!') % (
                    rule.name, )
                raise ProcurementException([(procurement, msg)])

            if rule.procure_method == 'mts_else_mto':
                mtso_products_by_locations[rule.location_src_id].append(
                    procurement.product_id.id)

        # Get the forecasted quantity for the `mts_else_mto` procurement.
        forecasted_qties_by_loc = {}
        for location, product_ids in mtso_products_by_locations.items():
            products = self.env['product.product'].browse(
                product_ids).with_context(location=location.id)
            forecasted_qties_by_loc[location] = {
                product.id: product.free_qty
                for product in products
            }

        # Prepare the move values, adapt the `procure_method` if needed.
        for procurement, rule in procurements:
            procure_method = rule.procure_method
            if rule.procure_method == 'mts_else_mto':
                qty_needed = procurement.product_uom._compute_quantity(
                    procurement.product_qty, procurement.product_id.uom_id)
                qty_available = forecasted_qties_by_loc[rule.location_src_id][
                    procurement.product_id.id]
                if float_compare(qty_needed,
                                 qty_available,
                                 precision_rounding=procurement.product_id.
                                 uom_id.rounding) <= 0:
                    procure_method = 'make_to_stock'
                    forecasted_qties_by_loc[rule.location_src_id][
                        procurement.product_id.id] -= qty_needed
                else:
                    procure_method = 'make_to_order'

            move_values = rule._get_stock_move_values(*procurement)
            move_values['procure_method'] = procure_method
            moves_values_by_company[procurement.company_id.id].append(
                move_values)

        for company_id, moves_values in moves_values_by_company.items():
            # create the move as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)
            moves = self.env['stock.move'].with_user(SUPERUSER_ID).sudo(
            ).with_company(company_id).create(moves_values)
            # Since action_confirm launch following procurement_group we should activate it.
            moves._action_confirm()
        return True
Exemplo n.º 28
0
 def test_pls_no_share_stage(self):
     """ We test here the situation where all stages are team specific, as there is
         a current limitation (can be seen in _pls_get_won_lost_total_count) regarding 
         the first stage (used to know how many lost and won there is) that requires 
         to have no team assigned to it."""
     Lead = self.env['crm.lead']
     team_id = self.env['crm.team'].create([{'name': 'Team Test'}]).id
     self.env['crm.stage'].search([('team_id', '=', False)
                                   ]).write({'team_id': team_id})
     lead = Lead.create({
         'name': 'team',
         'team_id': team_id,
         'probability': 41.23
     })
     Lead._cron_update_automated_probabilities()
     self.assertEqual(tools.float_compare(lead.probability, 41.23, 2), 0)
     self.assertEqual(tools.float_compare(lead.automated_probability, 0, 2),
                      0)
Exemplo n.º 29
0
 def _check_main_currency_rounding(self):
     if any(precision.name == 'Account' and tools.float_compare(
             self.env.user.company_id.currency_id.rounding,
             10**-precision.digits,
             precision_digits=6) == -1 for precision in self):
         raise ValidationError(
             _("You cannot define the decimal precision of 'Account' as greater than the rounding factor of the company's main currency"
               ))
     return True
Exemplo n.º 30
0
 def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False):
     res = super(AccountMoveLine, self).reconcile(writeoff_acc_id=writeoff_acc_id, writeoff_journal_id=writeoff_journal_id)
     account_move_ids = [l.move_id.id for l in self if float_compare(l.move_id.matched_percentage, 1, precision_digits=5) == 0]
     if account_move_ids:
         expense_sheets = self.env['hr.expense.sheet'].search([
             ('account_move_id', 'in', account_move_ids), ('state', '!=', 'done')
         ])
         expense_sheets.set_to_paid()
     return res