Beispiel #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,
                lang=self.order_id.partner_id.lang or self.env.user.lang or 'en_US'
            )
            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 {}
Beispiel #2
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.'))
Beispiel #3
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)
Beispiel #4
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)
         )
Beispiel #5
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'
         }
Beispiel #6
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'))
     if float_compare(available_qty, self.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'
         }
Beispiel #7
0
    def create_move(self, post_move=True):
        created_moves = self.env['account.move']
        prec = self.env['decimal.precision'].precision_get('Account')
        for line in self:
            if line.move_id:
                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,
                '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]
Beispiel #8
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
Beispiel #9
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
Beispiel #10
0
 def _update_line_quantity(self, values):
     precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
     if self.mapped('qty_delivered') and float_compare(values['product_uom_qty'], max(self.mapped('qty_delivered')), precision_digits=precision) == -1:
         raise UserError('You cannot decrease the ordered quantity below the delivered quantity.\n'
                         'Create a return first.')
     for line in self:
         pickings = line.order_id.picking_ids.filtered(lambda p: p.state not in ('done', 'cancel'))
         for picking in pickings:
             picking.message_post("The quantity of %s has been updated from %d to %d in %s" %
                                   (line.product_id.display_name, line.product_uom_qty, values['product_uom_qty'], line.order_id.name))
     super(SaleOrderLine, self)._update_line_quantity(values)
Beispiel #11
0
 def write(self, values):
     lines = self.env['sale.order.line']
     if 'product_uom_qty' in values:
         precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
         lines = self.filtered(
             lambda r: r.state == 'sale' and float_compare(r.product_uom_qty, values['product_uom_qty'], precision_digits=precision) == -1)
     previous_product_uom_qty = {line.id: line.product_uom_qty for line in lines}
     res = super(SaleOrderLine, self).write(values)
     if lines:
         lines.with_context(previous_product_uom_qty=previous_product_uom_qty)._action_launch_procurement_rule()
     return res
Beispiel #12
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 = line._get_qty_procurement()
            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

            procurement_uom = line.product_uom
            quant_uom = line.product_id.uom_id
            get_param = self.env['ir.config_parameter'].sudo().get_param
            if procurement_uom.id != quant_uom.id and get_param('stock.propagate_uom') != '1':
                product_qty = line.product_uom._compute_quantity(product_qty, quant_uom, rounding_method='HALF-UP')
                procurement_uom = quant_uom

            try:
                self.env['procurement.group'].run(line.product_id, product_qty, procurement_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
Beispiel #13
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
Beispiel #14
0
 def _onchange_qty_done(self):
     """ When the user is encoding a produce line for a tracked product, we apply some logic to
     help him. This onchange will warn him if he set `qty_done` to a non-supported value.
     """
     res = {}
     if self.product_id.tracking == 'serial':
         if float_compare(self.qty_done,
                          1.0,
                          precision_rounding=self.move_id.product_id.uom_id.
                          rounding) != 0:
             message = _(
                 'You can only process 1.0 %s for products with unique serial number.'
             ) % self.product_id.uom_id.name
             res['warning'] = {'title': _('Warning'), 'message': message}
     return res
Beispiel #15
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
Beispiel #16
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'}
Beispiel #17
0
 def _onchange_qty_producing(self):
     """ Update stock.move.lot records, according to the new qty currently
     produced. """
     moves = self.move_raw_ids.filtered(lambda move: move.state not in ('done', 'cancel') and move.product_id.tracking != 'none' and move.product_id.id != self.production_id.product_id.id)
     for move in moves:
         move_lots = self.active_move_line_ids.filtered(lambda move_lot: move_lot.move_id == move)
         if not move_lots:
             continue
         rounding = move.product_uom.rounding
         new_qty = float_round(move.unit_factor * self.qty_producing, precision_rounding=rounding)
         if move.product_id.tracking == 'lot':
             move_lots[0].product_qty = new_qty
             move_lots[0].qty_done = new_qty
         elif move.product_id.tracking == 'serial':
             # Create extra pseudo record
             qty_todo = float_round(new_qty - sum(move_lots.mapped('qty_done')), precision_rounding=rounding)
             if float_compare(qty_todo, 0.0, precision_rounding=rounding) > 0:
                 while float_compare(qty_todo, 0.0, precision_rounding=rounding) > 0:
                     self.active_move_line_ids += self.env['stock.move.line'].new({
                         'move_id': move.id,
                         'product_id': move.product_id.id,
                         'lot_id': False,
                         'product_uom_qty': 0.0,
                         'product_uom_id': move.product_uom.id,
                         'qty_done': min(1.0, qty_todo),
                         'workorder_id': self.id,
                         'done_wo': False,
                         'location_id': move.location_id.id,
                         'location_dest_id': move.location_dest_id.id,
                         'date': move.date,
                     })
                     qty_todo -= 1
             elif float_compare(qty_todo, 0.0, precision_rounding=rounding) < 0:
                 qty_todo = abs(qty_todo)
                 for move_lot in move_lots:
                     if float_compare(qty_todo, 0, precision_rounding=rounding) <= 0:
                         break
                     if not move_lot.lot_id and float_compare(qty_todo, move_lot.qty_done, precision_rounding=rounding) >= 0:
                         qty_todo = float_round(qty_todo - move_lot.qty_done, precision_rounding=rounding)
                         self.active_move_line_ids -= move_lot  # Difference operator
                     else:
                         #move_lot.product_qty = move_lot.product_qty - qty_todo
                         if float_compare(move_lot.qty_done - qty_todo, 0, precision_rounding=rounding) == 1:
                             move_lot.qty_done = move_lot.qty_done - qty_todo
                         else:
                             move_lot.qty_done = 0
                         qty_todo = 0
Beispiel #18
0
    def compare_amounts(self, amount1, amount2):
        """Compare ``amount1`` and ``amount2`` after rounding them according to the
           given currency's precision..
           An amount is considered lower/greater than another amount if their rounded
           value is different. This is not the same as having a non-zero difference!

           For example 1.432 and 1.431 are equal at 2 digits precision,
           so this method would return 0.
           However 0.006 and 0.002 are considered different (returns 1) because
           they respectively round to 0.01 and 0.0, even though
           0.006-0.002 = 0.004 which would be considered zero at 2 digits precision.

           :param float amount1: first amount to compare
           :param float amount2: second amount to compare
           :return: (resp.) -1, 0 or 1, if ``amount1`` is (resp.) lower than,
                    equal to, or greater than ``amount2``, according to
                    ``currency``'s rounding.

           With the new API, call it like: ``currency.compare_amounts(amount1, amount2)``.
        """
        return tools.float_compare(amount1,
                                   amount2,
                                   precision_rounding=self.rounding)
Beispiel #19
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
Beispiel #20
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
Beispiel #21
0
 def _generate_lot_ids(self):
     """ Generate stock move lines """
     self.ensure_one()
     MoveLine = self.env['stock.move.line']
     tracked_moves = self.move_raw_ids.filtered(
         lambda move: move.state not in ('done', 'cancel') and move.product_id.tracking != 'none' and move.product_id != self.production_id.product_id and move.bom_line_id)
     for move in tracked_moves:
         qty = move.unit_factor * self.qty_producing
         if move.product_id.tracking == 'serial':
             while float_compare(qty, 0.0, precision_rounding=move.product_uom.rounding) > 0:
                 MoveLine.create({
                     'move_id': move.id,
                     'product_uom_qty': 0,
                     'product_uom_id': move.product_uom.id,
                     'qty_done': min(1, qty),
                     'production_id': self.production_id.id,
                     'workorder_id': self.id,
                     'product_id': move.product_id.id,
                     'done_wo': False,
                     'location_id': move.location_id.id,
                     'location_dest_id': move.location_dest_id.id,
                 })
                 qty -= 1
         else:
             MoveLine.create({
                 'move_id': move.id,
                 'product_uom_qty': 0,
                 'product_uom_id': move.product_uom.id,
                 'qty_done': qty,
                 'product_id': move.product_id.id,
                 'production_id': self.production_id.id,
                 'workorder_id': self.id,
                 'done_wo': False,
                 'location_id': move.location_id.id,
                 'location_dest_id': move.location_dest_id.id,
                 })
Beispiel #22
0
    def record_production(self):
        self.ensure_one()
        if self.qty_producing <= 0:
            raise UserError(_('Please set the quantity you are currently producing. It should be different from zero.'))

        if (self.production_id.product_id.tracking != 'none') and not self.final_lot_id and self.move_raw_ids:
            raise UserError(_('You should provide a lot/serial number for the final product'))

        # Update quantities done on each raw material line
        # For each untracked component without any 'temporary' move lines,
        # (the new workorder tablet view allows registering consumed quantities for untracked components)
        # we assume that only the theoretical quantity was used
        for move in self.move_raw_ids:
            if move.has_tracking == 'none' and (move.state not in ('done', 'cancel')) and move.bom_line_id\
                        and move.unit_factor and not move.move_line_ids.filtered(lambda ml: not ml.done_wo):
                rounding = move.product_uom.rounding
                if self.product_id.tracking != 'none':
                    qty_to_add = float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding)
                    move._generate_consumed_move_line(qty_to_add, self.final_lot_id)
                elif len(move._get_move_lines()) < 2:
                    move.quantity_done += float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding)
                else:
                    move._set_quantity_done(move.quantity_done + float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding))

        # Transfer quantities from temporary to final move lots or make them final
        for move_line in self.active_move_line_ids:
            # Check if move_line already exists
            if move_line.qty_done <= 0:  # rounding...
                move_line.sudo().unlink()
                continue
            if move_line.product_id.tracking != 'none' and not move_line.lot_id:
                raise UserError(_('You should provide a lot/serial number for a component'))
            # Search other move_line where it could be added:
            lots = self.move_line_ids.filtered(lambda x: (x.lot_id.id == move_line.lot_id.id) and (not x.lot_produced_id) and (not x.done_move) and (x.product_id == move_line.product_id))
            if lots:
                lots[0].qty_done += move_line.qty_done
                lots[0].lot_produced_id = self.final_lot_id.id
                move_line.sudo().unlink()
            else:
                move_line.lot_produced_id = self.final_lot_id.id
                move_line.done_wo = True

        # One a piece is produced, you can launch the next work order
        if self.next_work_order_id.state == 'pending':
            self.next_work_order_id.state = 'ready'

        self.move_line_ids.filtered(
            lambda move_line: not move_line.done_move and not move_line.lot_produced_id and move_line.qty_done > 0
        ).write({
            'lot_produced_id': self.final_lot_id.id,
            'lot_produced_qty': self.qty_producing
        })

        # If last work order, then post lots used
        # TODO: should be same as checking if for every workorder something has been done?
        if not self.next_work_order_id:
            production_move = self.production_id.move_finished_ids.filtered(
                                lambda x: (x.product_id.id == self.production_id.product_id.id) and (x.state not in ('done', 'cancel')))
            if production_move.product_id.tracking != 'none':
                move_line = production_move.move_line_ids.filtered(lambda x: x.lot_id.id == self.final_lot_id.id)
                if move_line:
                    move_line.product_uom_qty += self.qty_producing
                    move_line.qty_done += self.qty_producing
                else:
                    move_line.create({'move_id': production_move.id,
                             'product_id': production_move.product_id.id,
                             'lot_id': self.final_lot_id.id,
                             'product_uom_qty': self.qty_producing,
                             'product_uom_id': production_move.product_uom.id,
                             'qty_done': self.qty_producing,
                             'workorder_id': self.id,
                             'location_id': production_move.location_id.id,
                             'location_dest_id': production_move.location_dest_id.id,
                    })
            else:
                production_move.quantity_done += self.qty_producing

        if not self.next_work_order_id:
            for by_product_move in self.production_id.move_finished_ids.filtered(lambda x: (x.product_id.id != self.production_id.product_id.id) and (x.state not in ('done', 'cancel'))):
                if by_product_move.has_tracking != 'serial':
                    values = self._get_byproduct_move_line(by_product_move, self.qty_producing * by_product_move.unit_factor)
                    self.env['stock.move.line'].create(values)
                elif by_product_move.has_tracking == 'serial':
                    qty_todo = by_product_move.product_uom._compute_quantity(self.qty_producing * by_product_move.unit_factor, by_product_move.product_id.uom_id)
                    for i in range(0, int(float_round(qty_todo, precision_digits=0))):
                        values = self._get_byproduct_move_line(by_product_move, 1)
                        self.env['stock.move.line'].create(values)

        # Update workorder quantity produced
        self.qty_produced += self.qty_producing

        if self.final_lot_id:
            self.final_lot_id.use_next_on_work_order_id = self.next_work_order_id
            self.final_lot_id = False

        # Set a qty producing
        rounding = self.production_id.product_uom_id.rounding
        if float_compare(self.qty_produced, self.production_id.product_qty, precision_rounding=rounding) >= 0:
            self.qty_producing = 0
        elif self.production_id.product_id.tracking == 'serial':
            self._assign_default_final_lot_id()
            self.qty_producing = 1.0
            self._generate_lot_ids()
        else:
            self.qty_producing = float_round(self.production_id.product_qty - self.qty_produced, precision_rounding=rounding)
            self._generate_lot_ids()

        if self.next_work_order_id and self.production_id.product_id.tracking != 'none':
            self.next_work_order_id._assign_default_final_lot_id()

        if float_compare(self.qty_produced, self.production_id.product_qty, precision_rounding=rounding) >= 0:
            self.button_finish()
        return True
Beispiel #23
0
    def _generate_consumed_move_line(self, qty_to_add, final_lot, lot=False):
        if lot:
            move_lines = self.move_line_ids.filtered(
                lambda ml: ml.lot_id == lot and not ml.lot_produced_id)
        else:
            move_lines = self.move_line_ids.filtered(
                lambda ml: not ml.lot_id and not ml.lot_produced_id)

        # Sanity check: if the product is a serial number and `lot` is already present in the other
        # consumed move lines, raise.
        if lot and self.product_id.tracking == 'serial' and lot in self.move_line_ids.filtered(
                lambda ml: ml.qty_done).mapped('lot_id'):
            raise UserError(
                _('You cannot consume the same serial number twice. Please correct the serial numbers encoded.'
                  ))

        for ml in move_lines:
            rounding = ml.product_uom_id.rounding
            if float_compare(qty_to_add, 0, precision_rounding=rounding) <= 0:
                break
            quantity_to_process = min(qty_to_add,
                                      ml.product_uom_qty - ml.qty_done)
            qty_to_add -= quantity_to_process

            new_quantity_done = (ml.qty_done + quantity_to_process)
            if float_compare(new_quantity_done,
                             ml.product_uom_qty,
                             precision_rounding=rounding) >= 0:
                ml.write({
                    'qty_done': new_quantity_done,
                    'lot_produced_id': final_lot.id
                })
            else:
                new_qty_reserved = ml.product_uom_qty - new_quantity_done
                default = {
                    'product_uom_qty': new_quantity_done,
                    'qty_done': new_quantity_done,
                    'lot_produced_id': final_lot.id
                }
                ml.copy(default=default)
                ml.with_context(bypass_reservation_update=True).write({
                    'product_uom_qty':
                    new_qty_reserved,
                    'qty_done':
                    0
                })

        if float_compare(qty_to_add,
                         0,
                         precision_rounding=self.product_uom.rounding) > 0:
            # Search for a sub-location where the product is available. This might not be perfectly
            # correct if the quantity available is spread in several sub-locations, but at least
            # we should be closer to the reality. Anyway, no reservation is made, so it is still
            # possible to change it afterwards.
            quants = self.env['stock.quant']._gather(self.product_id,
                                                     self.location_id,
                                                     lot_id=lot,
                                                     strict=False)
            available_quantity = self.product_id.uom_id._compute_quantity(
                self.env['stock.quant']._get_available_quantity(
                    self.product_id,
                    self.location_id,
                    lot_id=lot,
                    strict=False), self.product_uom)
            location_id = False
            if float_compare(qty_to_add,
                             available_quantity,
                             precision_rounding=self.product_uom.rounding) < 0:
                location_id = quants.filtered(
                    lambda r: r.quantity > 0)[-1:].location_id

            vals = {
                'move_id':
                self.id,
                'product_id':
                self.product_id.id,
                'location_id':
                location_id.id if location_id else self.location_id.id,
                'location_dest_id':
                self.location_dest_id.id,
                'product_uom_qty':
                0,
                'product_uom_id':
                self.product_uom.id,
                'qty_done':
                qty_to_add,
                'lot_produced_id':
                final_lot.id,
            }
            if lot:
                vals.update({'lot_id': lot.id})
            self.env['stock.move.line'].create(vals)
Beispiel #24
0
 def _compute_is_produced(self):
     rounding = self.production_id.product_uom_id.rounding
     self.is_produced = float_compare(self.qty_produced, self.production_id.product_qty, precision_rounding=rounding) >= 0
Beispiel #25
0
    def action_payslip_done(self):
        res = super(HrPayslip, self).action_payslip_done()
        precision = self.env['decimal.precision'].precision_get('Payroll')

        for slip in self:
            line_ids = []
            debit_sum = 0.0
            credit_sum = 0.0
            date = slip.date or slip.date_to

            name = _('Payslip of %s') % (slip.employee_id.name)
            move_dict = {
                'narration': name,
                'ref': slip.number,
                'journal_id': slip.journal_id.id,
                'date': date,
            }
            for line in slip.details_by_salary_rule_category:
                amount = slip.credit_note and -line.total or line.total
                if float_is_zero(amount, precision_digits=precision):
                    continue
                debit_account_id = line.salary_rule_id.account_debit.id
                credit_account_id = line.salary_rule_id.account_credit.id

                if debit_account_id:
                    debit_line = (0, 0, {
                        'name':
                        line.name,
                        'partner_id':
                        line._get_partner_id(credit_account=False),
                        'account_id':
                        debit_account_id,
                        'journal_id':
                        slip.journal_id.id,
                        'date':
                        date,
                        'debit':
                        amount > 0.0 and amount or 0.0,
                        'credit':
                        amount < 0.0 and -amount or 0.0,
                        'analytic_account_id':
                        line.salary_rule_id.analytic_account_id.id,
                        'tax_line_id':
                        line.salary_rule_id.account_tax_id.id,
                    })
                    line_ids.append(debit_line)
                    debit_sum += debit_line[2]['debit'] - debit_line[2][
                        'credit']

                if credit_account_id:
                    credit_line = (0, 0, {
                        'name':
                        line.name,
                        'partner_id':
                        line._get_partner_id(credit_account=True),
                        'account_id':
                        credit_account_id,
                        'journal_id':
                        slip.journal_id.id,
                        'date':
                        date,
                        'debit':
                        amount < 0.0 and -amount or 0.0,
                        'credit':
                        amount > 0.0 and amount or 0.0,
                        'analytic_account_id':
                        line.salary_rule_id.analytic_account_id.id,
                        'tax_line_id':
                        line.salary_rule_id.account_tax_id.id,
                    })
                    line_ids.append(credit_line)
                    credit_sum += credit_line[2]['credit'] - credit_line[2][
                        'debit']

            if float_compare(credit_sum, debit_sum,
                             precision_digits=precision) == -1:
                acc_id = slip.journal_id.default_credit_account_id.id
                if not acc_id:
                    raise UserError(
                        _('The Expense Journal "%s" has not properly configured the Credit Account!'
                          ) % (slip.journal_id.name))
                adjust_credit = (0, 0, {
                    'name': _('Adjustment Entry'),
                    'partner_id': False,
                    'account_id': acc_id,
                    'journal_id': slip.journal_id.id,
                    'date': date,
                    'debit': 0.0,
                    'credit': debit_sum - credit_sum,
                })
                line_ids.append(adjust_credit)

            elif float_compare(debit_sum,
                               credit_sum,
                               precision_digits=precision) == -1:
                acc_id = slip.journal_id.default_debit_account_id.id
                if not acc_id:
                    raise UserError(
                        _('The Expense Journal "%s" has not properly configured the Debit Account!'
                          ) % (slip.journal_id.name))
                adjust_debit = (0, 0, {
                    'name': _('Adjustment Entry'),
                    'partner_id': False,
                    'account_id': acc_id,
                    'journal_id': slip.journal_id.id,
                    'date': date,
                    'debit': credit_sum - debit_sum,
                    'credit': 0.0,
                })
                line_ids.append(adjust_debit)
            move_dict['line_ids'] = line_ids
            move = self.env['account.move'].create(move_dict)
            slip.write({'move_id': move.id, 'date': date})
            move.post()
        return res
    def test_order_to_invoice(self):

        # I create a new PoS order with 2 units of PC1 at 450 EUR (Tax Incl) and 3 units of PCSC349 at 300 EUR. (Tax Excl)
        self.pos_order_pos1 = self.PosOrder.create({
            'company_id': self.company_id,
            'partner_id': self.partner1.id,
            'pricelist_id': self.partner1.property_product_pricelist.id,
            'lines': [(0, 0, {
                'name': "OL/0001",
                'product_id': self.product3.id,
                'price_unit': 450,
                'discount': 5.0,
                'qty': 2.0,
                'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
            }), (0, 0, {
                'name': "OL/0002",
                'product_id': self.product4.id,
                'price_unit': 300,
                'discount': 5.0,
                'qty': 3.0,
                'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
            })]
        })

        # I click on the "Make Payment" wizard to pay the PoS order
        context_make_payment = {"active_ids": [self.pos_order_pos1.id], "active_id": self.pos_order_pos1.id}
        self.pos_make_payment = self.PosMakePayment.with_context(context_make_payment).create({
            'amount': (450 * 2 + 300 * 3 * 1.05) * 0.95,
        })
        # I click on the validate button to register the payment.
        context_payment = {'active_id': self.pos_order_pos1.id}
        self.pos_make_payment.with_context(context_payment).check()

        # I check that the order is marked as paid and there is no invoice
        # attached to it
        self.assertEqual(self.pos_order_pos1.state, 'paid', "Order should be in paid state.")
        self.assertFalse(self.pos_order_pos1.invoice_id, 'Invoice should not be attached to order.')

        # I generate an invoice from the order
        res = self.pos_order_pos1.action_pos_order_invoice()
        self.assertIn('res_id', res, "No invoice created")

        # I test that the total of the attached invoice is correct
        invoice = self.env['account.invoice'].browse(res['res_id'])
        self.assertEqual(
            float_compare(invoice.amount_total, 1752.75, precision_digits=2), 0, "Invoice not correct")

        """In order to test the reports on Bank Statement defined in point_of_sale module, I create a bank statement line, confirm it and print the reports"""

        # I select the period and journal for the bank statement

        context_journal = {'journal_type': 'bank'}
        self.assertTrue(self.AccountBankStatement.with_context(
            context_journal)._default_journal(), 'Journal has not been selected')
        journal = self.env['account.journal'].create({
            'name': 'Bank Test',
            'code': 'BNKT',
            'type': 'bank',
            'company_id': self.company_id,
        })
        # I create a bank statement with Opening and Closing balance 0.
        account_statement = self.AccountBankStatement.create({
            'balance_start': 0.0,
            'balance_end_real': 0.0,
            'date': time.strftime('%Y-%m-%d'),
            'journal_id': journal.id,
            'company_id': self.company_id,
            'name': 'pos session test',
        })
        # I create bank statement line
        account_statement_line = self.AccountBankStatementLine.create({
            'amount': 1000,
            'partner_id': self.partner4.id,
            'statement_id': account_statement.id,
            'name': 'EXT001'
        })
        # I modify the bank statement and set the Closing Balance.
        account_statement.write({
            'balance_end_real': 1000.0,
        })

        # I reconcile the bank statement.
        new_aml_dicts = [{
            'account_id': self.partner4.property_account_receivable_id.id,
            'name': "EXT001",
            'credit': 1000.0,
            'debit': 0.0,
        }]

        account_statement_line.process_reconciliations([{'new_aml_dicts': new_aml_dicts}])

        # I confirm the bank statement using Confirm button

        self.AccountBankStatement.button_confirm_bank()
Beispiel #27
0
    def _procure_orderpoint_confirm(self,
                                    use_new_cursor=False,
                                    company_id=False):
        """ Create procurements based on orderpoints.
        :param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing
            1000 orderpoints.
            This is appropriate for batch jobs only.
        """
        if company_id and self.env.user.company_id.id != company_id:
            # To ensure that the company_id is taken into account for
            # all the processes triggered by this method
            # i.e. If a PO is generated by the run of the procurements the
            # sequence to use is the one for the specified company not the
            # one of the user's company
            self = self.with_context(company_id=company_id,
                                     force_company=company_id)
        OrderPoint = self.env['stock.warehouse.orderpoint']
        domain = self._get_orderpoint_domain(company_id=company_id)
        orderpoints_noprefetch = OrderPoint.with_context(
            prefetch_fields=False).search(
                domain,
                order=self._procurement_from_orderpoint_get_order()).ids
        while orderpoints_noprefetch:
            if use_new_cursor:
                cr = registry(self._cr.dbname).cursor()
                self = self.with_env(self.env(cr=cr))
            OrderPoint = self.env['stock.warehouse.orderpoint']

            orderpoints = OrderPoint.browse(orderpoints_noprefetch[:1000])
            orderpoints_noprefetch = orderpoints_noprefetch[1000:]

            # Calculate groups that can be executed together
            location_data = OrderedDict()

            def makedefault():
                return {
                    'products': self.env['product.product'],
                    'orderpoints': self.env['stock.warehouse.orderpoint'],
                    'groups': []
                }

            for orderpoint in orderpoints:
                key = self._procurement_from_orderpoint_get_grouping_key(
                    [orderpoint.id])
                if not location_data.get(key):
                    location_data[key] = makedefault()
                location_data[key]['products'] += orderpoint.product_id
                location_data[key]['orderpoints'] += orderpoint
                location_data[key][
                    'groups'] = self._procurement_from_orderpoint_get_groups(
                        [orderpoint.id])

            for location_id, location_data in location_data.items():
                location_orderpoints = location_data['orderpoints']
                product_context = dict(
                    self._context,
                    location=location_orderpoints[0].location_id.id)
                substract_quantity = location_orderpoints._quantity_in_progress(
                )

                for group in location_data['groups']:
                    if group.get('from_date'):
                        product_context['from_date'] = group[
                            'from_date'].strftime(
                                DEFAULT_SERVER_DATETIME_FORMAT)
                    if group['to_date']:
                        product_context['to_date'] = group['to_date'].strftime(
                            DEFAULT_SERVER_DATETIME_FORMAT)
                    product_quantity = location_data['products'].with_context(
                        product_context)._product_available()
                    for orderpoint in location_orderpoints:
                        try:
                            op_product_virtual = product_quantity[
                                orderpoint.product_id.id]['virtual_available']
                            if op_product_virtual is None:
                                continue
                            if float_compare(op_product_virtual,
                                             orderpoint.product_min_qty,
                                             precision_rounding=orderpoint.
                                             product_uom.rounding) <= 0:
                                qty = max(orderpoint.product_min_qty,
                                          orderpoint.product_max_qty
                                          ) - op_product_virtual
                                remainder = orderpoint.qty_multiple > 0 and qty % orderpoint.qty_multiple or 0.0

                                if float_compare(remainder,
                                                 0.0,
                                                 precision_rounding=orderpoint.
                                                 product_uom.rounding) > 0:
                                    qty += orderpoint.qty_multiple - remainder

                                if float_compare(qty,
                                                 0.0,
                                                 precision_rounding=orderpoint.
                                                 product_uom.rounding) < 0:
                                    continue

                                qty -= substract_quantity[orderpoint.id]
                                qty_rounded = float_round(
                                    qty,
                                    precision_rounding=orderpoint.product_uom.
                                    rounding)
                                if qty_rounded > 0:
                                    values = orderpoint._prepare_procurement_values(
                                        qty_rounded,
                                        **group['procurement_values'])
                                    try:
                                        with self._cr.savepoint():
                                            self.env['procurement.group'].run(
                                                orderpoint.product_id,
                                                qty_rounded,
                                                orderpoint.product_uom,
                                                orderpoint.location_id,
                                                orderpoint.name,
                                                orderpoint.name, values)
                                    except UserError as error:
                                        self.env[
                                            'procurement.rule']._log_next_activity(
                                                orderpoint.product_id,
                                                error.name)
                                    self._procurement_from_orderpoint_post_process(
                                        [orderpoint.id])
                                if use_new_cursor:
                                    cr.commit()

                        except OperationalError:
                            if use_new_cursor:
                                orderpoints_noprefetch += [orderpoint.id]
                                cr.rollback()
                                continue
                            else:
                                raise

            try:
                if use_new_cursor:
                    cr.commit()
            except OperationalError:
                if use_new_cursor:
                    cr.rollback()
                    continue
                else:
                    raise

            if use_new_cursor:
                cr.commit()
                cr.close()

        return {}
    def test_10_calculation_price_of_products_pricelist(self):
        """Test calculation of product price based on pricelist"""
        # I check sale price of iPad Retina Display
        context = {}
        context.update({
            'pricelist': self.customer_pricelist.id,
            'quantity': 1
        })
        ipad_retina_display = self.ipad_retina_display.with_context(context)
        msg = "Wrong sale price: iPad Retina Display. should be %s instead of %s" % (
            ipad_retina_display.price,
            (ipad_retina_display.lst_price - ipad_retina_display.lst_price *
             (0.10)))
        self.assertEqual(
            float_compare(ipad_retina_display.price,
                          (ipad_retina_display.lst_price -
                           ipad_retina_display.lst_price * (0.10)),
                          precision_digits=2), 0, msg)

        # I check sale price of Laptop.
        laptop_E5023 = self.laptop_E5023.with_context(context)
        msg = "Wrong sale price: Laptop. should be %s instead of %s" % (
            laptop_E5023.price, (laptop_E5023.lst_price + 1))
        self.assertEqual(
            float_compare(laptop_E5023.price,
                          laptop_E5023.lst_price + 1,
                          precision_digits=2), 0, msg)

        # I check sale price of IT component.
        apple_headphones = self.apple_in_ear_headphones.with_context(context)
        msg = "Wrong sale price: IT component. should be %s instead of %s" % (
            apple_headphones.price, apple_headphones.lst_price)
        self.assertEqual(
            float_compare(apple_headphones.price,
                          apple_headphones.lst_price,
                          precision_digits=2), 0, msg)

        # I check sale price of IT component if more than 3 Unit.
        context.update({'quantity': 5})
        laptop_S3450 = self.laptop_S3450.with_context(context)
        msg = "Wrong sale price: IT component if more than 3 Unit. should be %s instead of %s" % (
            laptop_S3450.price,
            (laptop_S3450.lst_price - laptop_S3450.lst_price * (0.05)))
        self.assertEqual(
            float_compare(laptop_S3450.price,
                          laptop_S3450.lst_price - laptop_S3450.lst_price *
                          (0.05),
                          precision_digits=2), 0, msg)

        # I check sale price of LCD Monitor.
        context.update({'quantity': 1})
        ipad_mini = self.ipad_mini.with_context(context)
        msg = "Wrong sale price: LCD Monitor. should be %s instead of %s" % (
            ipad_mini.price, ipad_mini.lst_price)
        self.assertEqual(
            float_compare(ipad_mini.price,
                          ipad_mini.lst_price,
                          precision_digits=2), 0, msg)

        # I check sale price of LCD Monitor on end of year.
        context.update({'quantity': 1, 'date': '2011-12-31'})
        ipad_mini = self.ipad_mini.with_context(context)
        msg = "Wrong sale price: LCD Monitor on end of year. should be %s instead of %s" % (
            ipad_mini.price, ipad_mini.lst_price - ipad_mini.lst_price *
            (0.30))
        self.assertEqual(
            float_compare(ipad_mini.price,
                          ipad_mini.lst_price - ipad_mini.lst_price * (0.30),
                          precision_digits=2), 0, msg)

        # I check cost price of LCD Monitor.
        context.update({
            'quantity': 1,
            'date': False,
            'partner_id': self.res_partner_4.id
        })
        ipad_mini = self.ipad_mini.with_context(context)
        partner = self.res_partner_4.with_context(context)
        msg = "Wrong cost price: LCD Monitor. should be 790 instead of %s" % ipad_mini._select_seller(
            partner_id=partner, quantity=1.0).price
        self.assertEqual(
            float_compare(ipad_mini._select_seller(partner_id=partner,
                                                   quantity=1.0).price,
                          790,
                          precision_digits=2), 0, msg)

        # I check cost price of LCD Monitor if more than 3 Unit.
        context.update({'quantity': 3})
        ipad_mini = self.ipad_mini.with_context(context)
        partner = self.res_partner_4.with_context(context)
        msg = "Wrong cost price: LCD Monitor if more than 3 Unit.should be 785 instead of %s" % ipad_mini._select_seller(
            partner_id=partner, quantity=3.0).price
        self.assertEqual(
            float_compare(ipad_mini._select_seller(partner_id=partner,
                                                   quantity=3.0).price,
                          785,
                          precision_digits=2), 0, msg)

        # I print the sale prices report.
        ctx = {
            'active_model':
            'product.product',
            'date':
            '2011-12-30',
            'active_ids': [
                self.computer_SC234.id, self.ipad_retina_display.id,
                self.custom_computer_kit.id, self.ipad_mini.id
            ]
        }
        data_dict = {
            'qty1': 1,
            'qty2': 5,
            'qty3': 10,
            'qty4': 15,
            'qty5': 30,
            'price_list': self.customer_pricelist.id,
        }
        test_reports.try_report_action(self.cr,
                                       self.uid,
                                       'action_product_price_list',
                                       wiz_data=data_dict,
                                       context=ctx,
                                       our_module='product')
Beispiel #29
0
    def action_repair_done(self):
        """ Creates stock move for operation and stock move for final product of repair order.
        @return: Move ids of final products

        """
        if self.filtered(lambda repair: not repair.repaired):
            raise UserError(
                _("Repair must be repaired in order to make the product moves."
                  ))
        res = {}
        precision = self.env['decimal.precision'].precision_get(
            'Product Unit of Measure')
        Move = self.env['stock.move']
        for repair in self:
            # Try to create move with the appropriate owner
            owner_id = False
            available_qty_owner = self.env[
                'stock.quant']._get_available_quantity(
                    repair.product_id,
                    repair.location_id,
                    repair.lot_id,
                    owner_id=repair.partner_id,
                    strict=True)
            if float_compare(available_qty_owner,
                             repair.product_qty,
                             precision_digits=precision) >= 0:
                owner_id = repair.partner_id.id

            moves = self.env['stock.move']
            for operation in repair.operations:
                move = Move.create({
                    'name':
                    repair.name,
                    'product_id':
                    operation.product_id.id,
                    'product_uom_qty':
                    operation.product_uom_qty,
                    'product_uom':
                    operation.product_uom.id,
                    'partner_id':
                    repair.address_id.id,
                    'location_id':
                    operation.location_id.id,
                    'location_dest_id':
                    operation.location_dest_id.id,
                    'move_line_ids': [(
                        0,
                        0,
                        {
                            'product_id': operation.product_id.id,
                            'lot_id': operation.lot_id.id,
                            'product_uom_qty': 0,  # bypass reservation here
                            'product_uom_id': operation.product_uom.id,
                            'qty_done': operation.product_uom_qty,
                            'package_id': False,
                            'result_package_id': False,
                            'owner_id': owner_id,
                            'location_id':
                            operation.location_id.id,  #TODO: owner stuff
                            'location_dest_id': operation.location_dest_id.id,
                        })],
                    'repair_id':
                    repair.id,
                    'origin':
                    repair.name,
                })
                moves |= move
                operation.write({'move_id': move.id, 'state': 'done'})
            move = Move.create({
                'name':
                repair.name,
                'product_id':
                repair.product_id.id,
                'product_uom':
                repair.product_uom.id or repair.product_id.uom_id.id,
                'product_uom_qty':
                repair.product_qty,
                'partner_id':
                repair.address_id.id,
                'location_id':
                repair.location_id.id,
                'location_dest_id':
                repair.location_dest_id.id,
                'move_line_ids': [(
                    0,
                    0,
                    {
                        'product_id':
                        repair.product_id.id,
                        'lot_id':
                        repair.lot_id.id,
                        'product_uom_qty':
                        0,  # bypass reservation here
                        'product_uom_id':
                        repair.product_uom.id or repair.product_id.uom_id.id,
                        'qty_done':
                        repair.product_qty,
                        'package_id':
                        False,
                        'result_package_id':
                        False,
                        'owner_id':
                        owner_id,
                        'location_id':
                        repair.location_id.id,  #TODO: owner stuff
                        'location_dest_id':
                        repair.location_dest_id.id,
                    })],
                'repair_id':
                repair.id,
                'origin':
                repair.name,
            })
            consumed_lines = moves.mapped('move_line_ids')
            produced_lines = move.move_line_ids
            moves |= move
            moves._action_done()
            produced_lines.write(
                {'consume_line_ids': [(6, 0, consumed_lines.ids)]})
            res[repair.id] = move.id
        return res
Beispiel #30
0
    def test_00_delivery_cost(self):
        # In order to test Carrier Cost
        # Create sales order with Normal Delivery Charges

        self.sale_normal_delivery_charges = self.SaleOrder.create({
            'partner_id': self.partner_18.id,
            'partner_invoice_id': self.partner_18.id,
            'partner_shipping_id': self.partner_18.id,
            'pricelist_id': self.pricelist.id,
            'order_line': [(0, 0, {
                'name': 'PC Assamble + 2GB RAM',
                'product_id': self.product_4.id,
                'product_uom_qty': 1,
                'product_uom': self.product_uom_unit.id,
                'price_unit': 750.00,
            })],
            'carrier_id': self.normal_delivery.id
        })
        # I add delivery cost in Sales order

        self.a_sale = self.AccountAccount.create({
            'code': 'X2020',
            'name': 'Product Sales - (test)',
            'user_type_id': self.account_data.id,
            'tag_ids': [(6, 0, {
                self.account_tag_operating.id
            })]
        })

        self.product_consultant = self.Product.create({
            'sale_ok': True,
            'list_price': 75.0,
            'standard_price': 30.0,
            'uom_id': self.product_uom_hour.id,
            'uom_po_id': self.product_uom_hour.id,
            'name': 'Service',
            'categ_id': self.product_category.id,
            'type': 'service'
        })

        # I add delivery cost in Sales order
        self.sale_normal_delivery_charges.get_delivery_price()
        self.sale_normal_delivery_charges.set_delivery_line()

        # I check sales order after added delivery cost

        line = self.SaleOrderLine.search([('order_id', '=', self.sale_normal_delivery_charges.id),
            ('product_id', '=', self.sale_normal_delivery_charges.carrier_id.product_id.id)])
        self.assertEqual(len(line), 1, "Delivery cost is not Added")

        self.assertEqual(float_compare(line.price_subtotal, 10.0, precision_digits=2), 0,
            "Delivery cost is not correspond.")

        # I confirm the sales order

        self.sale_normal_delivery_charges.action_confirm()

        # Create one more sales order with Free Delivery Charges

        self.delivery_sale_order_cost = self.SaleOrder.create({
            'partner_id': self.partner_4.id,
            'partner_invoice_id': self.partner_address_13.id,
            'partner_shipping_id': self.partner_address_13.id,
            'pricelist_id': self.pricelist.id,
            'order_line': [(0, 0, {
                'name': 'Service on demand',
                'product_id': self.product_consultant.id,
                'product_uom_qty': 24,
                'product_uom': self.product_uom_hour.id,
                'price_unit': 75.00,
            }), (0, 0, {
                'name': 'On Site Assistance',
                'product_id': self.product_2.id,
                'product_uom_qty': 30,
                'product_uom': self.product_uom_hour.id,
                'price_unit': 38.25,
            })],
            'carrier_id': self.free_delivery.id
        })

        # I add free delivery cost in Sales order
        self.delivery_sale_order_cost.get_delivery_price()
        self.delivery_sale_order_cost.set_delivery_line()

        # I check sales order after adding delivery cost
        line = self.SaleOrderLine.search([('order_id', '=', self.delivery_sale_order_cost.id),
            ('product_id', '=', self.delivery_sale_order_cost.carrier_id.product_id.id)])

        self.assertEqual(len(line), 1, "Delivery cost is not Added")
        self.assertEqual(float_compare(line.price_subtotal, 0, precision_digits=2), 0,
            "Delivery cost is not correspond.")

        # I set default delivery policy

        self.default_delivery_policy = self.SaleConfigSetting.create({})

        self.default_delivery_policy.execute()