def action_put_in_pack(self):
        picking_move_lines = self.picking_id.move_line_ids
        if not self.picking_id.picking_type_id.show_reserved and not self.env.context.get(
                'barcode_view'):
            picking_move_lines = self.picking_id.move_line_nosuggest_ids

        move_line_ids = picking_move_lines.filtered(lambda ml: float_compare(
            ml.qty_done, 0.0, precision_rounding=ml.product_uom_id.rounding
        ) > 0 and not ml.result_package_id)
        if not move_line_ids:
            move_line_ids = picking_move_lines.filtered(
                lambda ml: float_compare(ml.product_uom_qty,
                                         0.0,
                                         precision_rounding=ml.product_uom_id.
                                         rounding) > 0 and
                float_compare(ml.qty_done,
                              0.0,
                              precision_rounding=ml.product_uom_id.rounding
                              ) == 0)

        delivery_package = self.picking_id._put_in_pack(move_line_ids)
        # write shipping weight and product_packaging on 'stock_quant_package' if needed
        if self.delivery_packaging_id:
            delivery_package.packaging_id = self.delivery_packaging_id
        if self.shipping_weight:
            delivery_package.shipping_weight = self.shipping_weight
Пример #2
0
 def _get_ddt_values(self):
     """
     We calculate the link between the invoice lines and the deliveries related to the invoice through the
     links with the sale order(s).  We assume that the first picking was invoiced first. (FIFO)
     :return: a dictionary with as key the picking and value the invoice line numbers (by counting)
     """
     self.ensure_one()
     # We don't consider returns/credit notes as we suppose they will lead to more deliveries/invoices as well
     if self.move_type != "out_invoice" or self.state != 'posted':
         return {}
     line_count = 0
     invoice_line_pickings = {}
     for line in self.invoice_line_ids.filtered(
             lambda l: not l.display_type):
         line_count += 1
         done_moves_related = line.sale_line_ids.mapped(
             'move_ids').filtered(lambda m: m.state == 'done' and m.
                                  location_dest_id.usage == 'customer')
         if len(done_moves_related) <= 1:
             if done_moves_related and line_count not in invoice_line_pickings.get(
                     done_moves_related.picking_id, []):
                 invoice_line_pickings.setdefault(
                     done_moves_related.picking_id, []).append(line_count)
         else:
             total_invoices = done_moves_related.mapped(
                 'sale_line_id.invoice_lines').filtered(
                     lambda l: l.move_id.state == 'posted' and l.move_id.
                     move_type == 'out_invoice').sorted(
                         lambda l: l.move_id.invoice_date)
             total_invs = [(i.product_uom_id._compute_quantity(
                 i.quantity, i.product_id.uom_id), i)
                           for i in total_invoices]
             inv = total_invs.pop(0)
             # Match all moves and related invoice lines FIFO looking for when the matched invoice_line matches line
             for move in done_moves_related.sorted(lambda m: m.date):
                 rounding = move.product_uom.rounding
                 move_qty = move.product_qty
                 while (float_compare(
                         move_qty, 0, precision_rounding=rounding) > 0):
                     if float_compare(inv[0],
                                      move_qty,
                                      precision_rounding=rounding) > 0:
                         inv = (inv[0] - move_qty, inv[1])
                         invoice_line = inv[1]
                         move_qty = 0
                     if float_compare(inv[0],
                                      move_qty,
                                      precision_rounding=rounding) <= 0:
                         move_qty -= inv[0]
                         invoice_line = inv[1]
                         if total_invs:
                             inv = total_invs.pop(0)
                         else:
                             move_qty = 0  #abort when not enough matched invoices
                     # If in our FIFO iteration we stumble upon the line we were checking
                     if invoice_line == line and line_count not in invoice_line_pickings.get(
                             move.picking_id, []):
                         invoice_line_pickings.setdefault(
                             move.picking_id, []).append(line_count)
     return invoice_line_pickings
Пример #3
0
    def action_put_in_pack(self):
        """ Action to put move lines with 'Done' quantities into a new pack
        This method follows same logic to stock.picking.
        """
        self.ensure_one()
        if self.state not in ('done', 'cancel'):
            picking_move_lines = self.move_line_ids

            move_line_ids = picking_move_lines.filtered(
                lambda ml: float_compare(ml.qty_done,
                                         0.0,
                                         precision_rounding=ml.product_uom_id.
                                         rounding) > 0 and not ml.
                result_package_id)
            if not move_line_ids:
                move_line_ids = picking_move_lines.filtered(
                    lambda ml: float_compare(ml.product_uom_qty,
                                             0.0,
                                             precision_rounding=ml.
                                             product_uom_id.rounding) > 0 and
                    float_compare(ml.qty_done,
                                  0.0,
                                  precision_rounding=ml.product_uom_id.rounding
                                  ) == 0)
            if move_line_ids:
                res = self.picking_ids[0]._pre_put_in_pack_hook(move_line_ids)
                if not res:
                    res = self.picking_ids[0]._put_in_pack(
                        move_line_ids, False)
                return res
            else:
                raise UserError(
                    _("Please add 'Done' quantities to the batch picking to create a new pack."
                      ))
Пример #4
0
    def _create_or_update_picking(self):
        for line in self:
            if line.product_id and line.product_id.type in ('product',
                                                            'consu'):
                # Prevent decreasing below received quantity
                if float_compare(line.product_qty, line.qty_received,
                                 line.product_uom.rounding) < 0:
                    raise UserError(
                        _('You cannot decrease the ordered quantity below the received quantity.\n'
                          'Create a return first.'))

                if float_compare(line.product_qty, line.qty_invoiced,
                                 line.product_uom.rounding) == -1:
                    # If the quantity is now below the invoiced quantity, create an activity on the vendor bill
                    # inviting the user to create a refund.
                    line.invoice_lines[0].move_id.activity_schedule(
                        'mail.mail_activity_data_warning',
                        note=
                        _('The quantities on your purchase order indicate less than billed. You should ask for a refund.'
                          ))

                # If the user increased quantity of existing line or created a new line
                pickings = line.order_id.picking_ids.filtered(
                    lambda x: x.state not in
                    ('done', 'cancel') and x.location_dest_id.usage in
                    ('internal', 'transit', 'customer'))
                picking = pickings and pickings[0] or False
                if not picking:
                    res = line.order_id._prepare_picking()
                    picking = self.env['stock.picking'].create(res)

                moves = line._create_stock_moves(picking)
                moves._action_confirm()._action_assign()
Пример #5
0
    def _paypal_form_get_invalid_parameters(self, data):
        invalid_parameters = []
        _logger.info('Received a notification from Paypal with IPN version %s', data.get('notify_version'))
        if data.get('test_ipn'):
            _logger.warning(
                'Received a notification from Paypal using sandbox'
            ),

        # TODO: txn_id: shoudl be false at draft, set afterwards, and verified with txn details
        if self.acquirer_reference and data.get('txn_id') != self.acquirer_reference:
            invalid_parameters.append(('txn_id', data.get('txn_id'), self.acquirer_reference))
        # check what is buyed
        if float_compare(float(data.get('mc_gross', '0.0')), (self.amount + self.fees), 2) != 0:
            invalid_parameters.append(('mc_gross', data.get('mc_gross'), '%.2f' % self.amount))  # mc_gross is amount + fees
        if data.get('mc_currency') != self.currency_id.name:
            invalid_parameters.append(('mc_currency', data.get('mc_currency'), self.currency_id.name))
        if 'handling_amount' in data and float_compare(float(data.get('handling_amount')), self.fees, 2) != 0:
            invalid_parameters.append(('handling_amount', data.get('handling_amount'), self.fees))
        # check buyer
        if self.payment_token_id and data.get('payer_id') != self.payment_token_id.acquirer_ref:
            invalid_parameters.append(('payer_id', data.get('payer_id'), self.payment_token_id.acquirer_ref))
        # check seller
        if data.get('receiver_id') and self.acquirer_id.paypal_seller_account and data['receiver_id'] != self.acquirer_id.paypal_seller_account:
            invalid_parameters.append(('receiver_id', data.get('receiver_id'), self.acquirer_id.paypal_seller_account))
        if not data.get('receiver_id') or not self.acquirer_id.paypal_seller_account:
            # Check receiver_email only if receiver_id was not checked.
            # In Paypal, this is possible to configure as receiver_email a different email than the business email (the login email)
            # In Flectra, there is only one field for the Paypal email: the business email. This isn't possible to set a receiver_email
            # different than the business email. Therefore, if you want such a configuration in your Paypal, you are then obliged to fill
            # the Merchant ID in the Paypal payment acquirer in Flectra, so the check is performed on this variable instead of the receiver_email.
            # At least one of the two checks must be done, to avoid fraudsters.
            if data.get('receiver_email') != self.acquirer_id.paypal_email_account:
                invalid_parameters.append(('receiver_email', data.get('receiver_email'), self.acquirer_id.paypal_email_account))

        return invalid_parameters
Пример #6
0
 def _has_components_to_record(self):
     """ Returns true if the move has still some tracked components to record. """
     self.ensure_one()
     if not self.is_subcontract:
         return False
     rounding = self.product_uom.rounding
     production = self.move_orig_ids.production_id[-1:]
     return self._has_tracked_subcontract_components() and\
         float_compare(production.qty_produced, production.product_uom_qty, precision_rounding=rounding) < 0 and\
         float_compare(self.quantity_done, self.product_uom_qty, precision_rounding=rounding) < 0
Пример #7
0
    def _prepare_stock_moves(self, picking):
        """ Prepare the stock moves data for one order line. This function returns a list of
        dictionary ready to be used in stock.move's create()
        """
        self.ensure_one()
        res = []
        if self.product_id.type not in ['product', 'consu']:
            return res

        price_unit = self._get_stock_move_price_unit()
        qty = self._get_qty_procurement()

        move_dests = self.move_dest_ids
        if not move_dests:
            move_dests = self.move_ids.move_dest_ids.filtered(
                lambda m: m.state != 'cancel' and not m.location_dest_id.usage
                == 'supplier')

        if not move_dests:
            qty_to_attach = 0
            qty_to_push = self.product_qty - qty
        else:
            move_dests_initial_demand = self.product_id.uom_id._compute_quantity(
                sum(
                    move_dests.filtered(lambda m: m.state != 'cancel' and not m
                                        .location_dest_id.usage == 'supplier').
                    mapped('product_qty')),
                self.product_uom,
                rounding_method='HALF-UP')
            qty_to_attach = min(self.product_qty,
                                move_dests_initial_demand) - qty
            qty_to_push = self.product_qty - move_dests_initial_demand

        if float_compare(qty_to_attach,
                         0.0,
                         precision_rounding=self.product_uom.rounding) > 0:
            product_uom_qty, product_uom = self.product_uom._adjust_uom_quantities(
                qty_to_attach, self.product_id.uom_id)
            res.append(
                self._prepare_stock_move_vals(picking, price_unit,
                                              product_uom_qty, product_uom))
        if float_compare(qty_to_push,
                         0.0,
                         precision_rounding=self.product_uom.rounding) > 0:
            product_uom_qty, product_uom = self.product_uom._adjust_uom_quantities(
                qty_to_push, self.product_id.uom_id)
            extra_move_vals = self._prepare_stock_move_vals(
                picking, price_unit, product_uom_qty, product_uom)
            extra_move_vals['move_dest_ids'] = False  # don't attach
            res.append(extra_move_vals)
        return res
    def process(self):
        pickings_to_do = self.env['stock.picking']
        pickings_not_to_do = self.env['stock.picking']
        for line in self.backorder_confirmation_line_ids:
            if line.to_backorder is True:
                pickings_to_do |= line.picking_id
            else:
                pickings_not_to_do |= line.picking_id

        for pick_id in pickings_not_to_do:
            moves_to_log = {}
            for move in pick_id.move_lines:
                if float_compare(
                        move.product_uom_qty,
                        move.quantity_done,
                        precision_rounding=move.product_uom.rounding) > 0:
                    moves_to_log[move] = (move.quantity_done,
                                          move.product_uom_qty)
            pick_id._log_less_quantities_than_expected(moves_to_log)

        pickings_to_validate = self.env.context.get(
            'button_validate_picking_ids')
        if pickings_to_validate:
            pickings_to_validate = self.env['stock.picking'].browse(
                pickings_to_validate).with_context(skip_backorder=True)
            if pickings_not_to_do:
                pickings_to_validate = pickings_to_validate.with_context(
                    picking_ids_not_to_backorder=pickings_not_to_do.ids)
            return pickings_to_validate.button_validate()
        return True
Пример #9
0
    def _put_in_pack(self):
        package = False
        for pick in self:
            operations = pick.move_line_ids.filtered(lambda o: o.qty_done > 0 and not o.result_package_id)
            operation_ids = self.env['stock.move.line']
            if operations:
                package = self.env['stock.quant.package'].create({})
                for operation in operations:
                    if float_compare(operation.qty_done, operation.product_uom_qty, precision_rounding=operation.product_uom_id.rounding) >= 0:
                        operation_ids |= operation
                    else:
                        quantity_left_todo = float_round(
                            operation.product_uom_qty - operation.qty_done,
                            precision_rounding=operation.product_uom_id.rounding,
                            rounding_method='UP')
                        done_to_keep = operation.qty_done
                        new_operation = operation.copy(
                            default={'product_uom_qty': 0, 'qty_done': operation.qty_done})
                        operation.write({'product_uom_qty': quantity_left_todo, 'qty_done': 0.0})
                        new_operation.write({'product_uom_qty': done_to_keep})
                        operation_ids |= new_operation

                operation_ids.write({'result_package_id': package.id})
            else:
                raise UserError(_('Please process some quantities to put in the pack first!'))
        return package
Пример #10
0
 def _prepare_invoice_line_from_po_line(self, line):
     if line.product_id.purchase_method == 'purchase':
         qty = line.product_qty - line.qty_invoiced
     else:
         qty = line.qty_received - line.qty_invoiced
     if float_compare(qty, 0.0, precision_rounding=line.product_uom.rounding) <= 0:
         qty = 0.0
     taxes = line.taxes_id
     invoice_line_tax_ids = line.order_id.fiscal_position_id.map_tax(taxes)
     invoice_line = self.env['account.invoice.line']
     data = {
         'purchase_line_id': line.id,
         'name': line.order_id.name+': '+line.name,
         'origin': line.order_id.origin,
         'uom_id': line.product_uom.id,
         'product_id': line.product_id.id,
         'account_id': invoice_line.with_context({'journal_id': self.journal_id.id, 'type': 'in_invoice'})._default_account(),
         'price_unit': line.order_id.currency_id.with_context(date=self.date_invoice).compute(line.price_unit, self.currency_id, round=False),
         'quantity': qty,
         'discount': 0.0,
         'account_analytic_id': line.account_analytic_id.id,
         'analytic_tag_ids': line.analytic_tag_ids.ids,
         'invoice_line_tax_ids': invoice_line_tax_ids.ids
     }
     account = invoice_line.get_invoice_line_account('in_invoice', line.product_id, line.order_id.fiscal_position_id, self.env.user.company_id)
     if account:
         data['account_id'] = account.id
     return data
Пример #11
0
    def _onchange_location_or_product_id(self):
        vals = {}

        # Once the new line is complete, fetch the new theoretical values.
        if self.product_id and self.location_id:
            # Sanity check if a lot has been set.
            if self.lot_id:
                if self.tracking == 'none' or self.product_id != self.lot_id.product_id:
                    vals['lot_id'] = None

            quants = self._gather(self.product_id,
                                  self.location_id,
                                  lot_id=self.lot_id,
                                  package_id=self.package_id,
                                  owner_id=self.owner_id,
                                  strict=True)
            reserved_quantity = sum(quants.mapped('reserved_quantity'))
            quantity = sum(quants.mapped('quantity'))

            vals['reserved_quantity'] = reserved_quantity
            # Update `quantity` only if the user manually updated `inventory_quantity`.
            if float_compare(
                    self.quantity,
                    self.inventory_quantity,
                    precision_rounding=self.product_uom_id.rounding) == 0:
                vals['quantity'] = quantity
            # Special case: directly set the quantity to one for serial numbers,
            # it'll trigger `inventory_quantity` compute.
            if self.lot_id and self.tracking == 'serial':
                vals['quantity'] = 1

        if vals:
            self.update(vals)
Пример #12
0
 def _get_overprocessed_stock_moves(self):
     self.ensure_one()
     return self.move_lines.filtered(
         lambda move: move.product_uom_qty != 0 and float_compare(
             move.quantity_done,
             move.product_uom_qty,
             precision_rounding=move.product_uom.rounding) == 1)
Пример #13
0
    def add_payment(self, data):
        statement_id = super(PosOrder, self).add_payment(data)
        statement_lines = self.env['account.bank.statement.line'].search([('statement_id', '=', statement_id),
                                                                         ('pos_statement_id', '=', self.id),
                                                                         ('journal_id', '=', data['journal'])])
        statement_lines = statement_lines.filtered(lambda line: float_compare(line.amount, data['amount'],
                                                                              precision_rounding=line.journal_currency_id.rounding) == 0)

        # we can get multiple statement_lines when there are >1 credit
        # card payments with the same amount. In that case it doesn't
        # matter which statement line we pick, just pick one that
        # isn't already used.
        for line in statement_lines:
            if not line.mercury_card_brand:
                line.mercury_card_brand = data.get('card_brand')
                line.mercury_card_number = data.get('card_number')
                line.mercury_card_owner_name = data.get('card_owner_name')

                line.mercury_ref_no = data.get('ref_no')
                line.mercury_record_no = data.get('record_no')
                line.mercury_invoice_no = data.get('invoice_no')

                break

        return statement_id
Пример #14
0
 def _set_inventory_quantity(self):
     """ Inverse method to create stock move when `inventory_quantity` is set
     (`inventory_quantity` is only accessible in inventory mode).
     """
     if not self._is_inventory_mode():
         return
     for quant in self:
         # Get the quantity to create a move for.
         rounding = quant.product_id.uom_id.rounding
         diff = float_round(quant.inventory_quantity - quant.quantity,
                            precision_rounding=rounding)
         diff_float_compared = float_compare(diff,
                                             0,
                                             precision_rounding=rounding)
         # Create and vaidate a move so that the quant matches its `inventory_quantity`.
         if diff_float_compared == 0:
             continue
         elif diff_float_compared > 0:
             move_vals = quant._get_inventory_move_values(
                 diff,
                 quant.product_id.with_company(
                     quant.company_id).property_stock_inventory,
                 quant.location_id)
         else:
             move_vals = quant._get_inventory_move_values(
                 -diff,
                 quant.location_id,
                 quant.product_id.with_company(
                     quant.company_id).property_stock_inventory,
                 out=True)
         move = quant.env['stock.move'].with_context(
             inventory_mode=False).create(move_vals)
         move._action_done()
Пример #15
0
    def _get_first_available_slot(self, start_datetime, duration):
        """Get the first available interval for the workcenter in `self`.

        The available interval is disjoinct with all other workorders planned on this workcenter, but
        can overlap the time-off of the related calendar (inverse of the working hours).
        Return the first available interval (start datetime, end datetime) or,
        if there is none before 700 days, a tuple error (False, 'error message').

        :param start_datetime: begin the search at this datetime
        :param duration: minutes needed to make the workorder (float)
        :rtype: tuple
        """
        self.ensure_one()
        start_datetime, revert = make_aware(start_datetime)

        get_available_intervals = partial(
            self.resource_calendar_id._work_intervals,
            domain=[('time_type', 'in', ['other', 'leave'])],
            resource=self.resource_id,
            tz=timezone(self.resource_calendar_id.tz))
        get_workorder_intervals = partial(
            self.resource_calendar_id._leave_intervals,
            domain=[('time_type', '=', 'other')],
            resource=self.resource_id,
            tz=timezone(self.resource_calendar_id.tz))

        remaining = duration
        start_interval = start_datetime
        delta = timedelta(days=14)

        for n in range(50):  # 50 * 14 = 700 days in advance (hardcoded)
            dt = start_datetime + delta * n
            available_intervals = get_available_intervals(dt, dt + delta)
            workorder_intervals = get_workorder_intervals(dt, dt + delta)
            for start, stop, dummy in available_intervals:
                # Shouldn't loop more than 2 times because the available_intervals contains the workorder_intervals
                # And remaining == duration can only occur at the first loop and at the interval intersection (cannot happen several time because available_intervals > workorder_intervals
                for i in range(2):
                    interval_minutes = (stop - start).total_seconds() / 60
                    # If the remaining minutes has never decrease update start_interval
                    if remaining == duration:
                        start_interval = start
                    # If there is a overlap between the possible available interval and a others WO
                    if Intervals(
                        [(start_interval, start +
                          timedelta(minutes=min(remaining, interval_minutes)),
                          dummy)]) & workorder_intervals:
                        remaining = duration
                    elif float_compare(interval_minutes,
                                       remaining,
                                       precision_digits=3) >= 0:
                        return revert(start_interval), revert(
                            start + timedelta(minutes=remaining))
                    else:
                        # Decrease a part of the remaining duration
                        remaining -= interval_minutes
                        # Go to the next available interval because the possible current interval duration has been used
                        break
        return False, 'Not available slot 700 days after the planned start'
Пример #16
0
 def _compute_show_check_availability(self):
     for picking in self:
         has_moves_to_reserve = any(
             move.state in ('waiting', 'confirmed', 'partially_available') and
             float_compare(move.product_uom_qty, 0, precision_rounding=move.product_uom.rounding)
             for move in picking.move_lines
         )
         picking.show_check_availability = picking.is_locked and picking.state in ('confirmed', 'waiting', 'assigned') and has_moves_to_reserve
Пример #17
0
    def _free_reservation(self, product_id, location_id, quantity, lot_id=None, package_id=None, owner_id=None, ml_to_ignore=None):
        """ When editing a done move line or validating one with some forced quantities, it is
        possible to impact quants that were not reserved. It is therefore necessary to edit or
        unlink the move lines that reserved a quantity now unavailable.

        :param ml_to_ignore: recordset of `stock.move.line` that should NOT be unreserved
        """
        self.ensure_one()

        if ml_to_ignore is None:
            ml_to_ignore = self.env['stock.move.line']
        ml_to_ignore |= self

        # Check the available quantity, with the `strict` kw set to `True`. If the available
        # quantity is greather than the quantity now unavailable, there is nothing to do.
        available_quantity = self.env['stock.quant']._get_available_quantity(
            product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=True
        )
        if quantity > available_quantity:
            # We now have to find the move lines that reserved our now unavailable quantity. We
            # take care to exclude ourselves and the move lines were work had already been done.
            oudated_move_lines_domain = [
                ('move_id.state', 'not in', ['done', 'cancel']),
                ('product_id', '=', product_id.id),
                ('lot_id', '=', lot_id.id if lot_id else False),
                ('location_id', '=', location_id.id),
                ('owner_id', '=', owner_id.id if owner_id else False),
                ('package_id', '=', package_id.id if package_id else False),
                ('product_qty', '>', 0.0),
                ('id', 'not in', ml_to_ignore.ids),
            ]
            oudated_candidates = self.env['stock.move.line'].search(oudated_move_lines_domain)

            # As the move's state is not computed over the move lines, we'll have to manually
            # recompute the moves which we adapted their lines.
            move_to_recompute_state = self.env['stock.move']

            rounding = self.product_uom_id.rounding
            for candidate in oudated_candidates:
                if float_compare(candidate.product_qty, quantity, precision_rounding=rounding) <= 0:
                    quantity -= candidate.product_qty
                    move_to_recompute_state |= candidate.move_id
                    if candidate.qty_done:
                        candidate.product_uom_qty = 0.0
                    else:
                        candidate.unlink()
                else:
                    # split this move line and assign the new part to our extra move
                    quantity_split = float_round(
                        candidate.product_qty - quantity,
                        precision_rounding=self.product_uom_id.rounding,
                        rounding_method='UP')
                    candidate.product_uom_qty = self.product_id.uom_id._compute_quantity(quantity_split, candidate.product_uom_id, rounding_method='HALF-UP')
                    quantity -= quantity_split
                    move_to_recompute_state |= candidate.move_id
                if quantity == 0.0:
                    break
            move_to_recompute_state._recompute_state()
Пример #18
0
    def _transfer_form_get_invalid_parameters(self, data):
        invalid_parameters = []

        if float_compare(float(data.get('amount', '0.0')), self.amount, 2) != 0:
            invalid_parameters.append(('amount', data.get('amount'), '%.2f' % self.amount))
        if data.get('currency') != self.currency_id.name:
            invalid_parameters.append(('currency', data.get('currency'), self.currency_id.name))

        return invalid_parameters
Пример #19
0
    def _authorize_form_get_invalid_parameters(self, data):
        invalid_parameters = []

        if self.acquirer_reference and data.get('x_trans_id') != self.acquirer_reference:
            invalid_parameters.append(('Transaction Id', data.get('x_trans_id'), self.acquirer_reference))
        # check what is buyed
        if float_compare(float(data.get('x_amount', '0.0')), self.amount, 2) != 0:
            invalid_parameters.append(('Amount', data.get('x_amount'), '%.2f' % self.amount))
        return invalid_parameters
Пример #20
0
 def check_quantity(self):
     for quant in self:
         if float_compare(
                 quant.quantity,
                 1,
                 precision_rounding=quant.product_uom_id.rounding
         ) > 0 and quant.lot_id and quant.product_id.tracking == 'serial':
             raise ValidationError(
                 _('A serial number should only be linked to a single product.'
                   ))
Пример #21
0
 def check_quantity(self):
     for quant in self:
         if float_compare(
                 quant.quantity,
                 1,
                 precision_rounding=quant.product_uom_id.rounding
         ) > 0 and quant.lot_id and quant.product_id.tracking == 'serial':
             raise ValidationError(
                 _('The serial number has already been assigned: \n Product: %s, Serial Number: %s'
                   ) % (quant.product_id.display_name, quant.lot_id.name))
Пример #22
0
 def _onchange_qty_done(self):
     """ When the user is encoding a move 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.qty_done and 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
Пример #23
0
 def _onchange_secondary_uom(self):
     if not self.secondary_uom_id:
         return
     factor = self.secondary_uom_id.factor * self.product_id.uom_id.factor
     qty = float_round(self.secondary_uom_qty * factor,
                       precision_rounding=self.product_id.uom_id.rounding)
     if float_compare(
             self.units_included,
             qty,
             precision_rounding=self.product_id.uom_id.rounding) != 0:
         self.units_included = qty
Пример #24
0
    def _buckaroo_form_get_invalid_parameters(self, data):
        invalid_parameters = []
        data = normalize_keys_upper(data)
        if self.acquirer_reference and data.get('BRQ_TRANSACTIONS') != self.acquirer_reference:
            invalid_parameters.append(('Transaction Id', data.get('BRQ_TRANSACTIONS'), self.acquirer_reference))
        # check what is buyed
        if float_compare(float(data.get('BRQ_AMOUNT', '0.0')), self.amount, 2) != 0:
            invalid_parameters.append(('Amount', data.get('BRQ_AMOUNT'), '%.2f' % self.amount))
        if data.get('BRQ_CURRENCY') != self.currency_id.name:
            invalid_parameters.append(('Currency', data.get('BRQ_CURRENCY'), self.currency_id.name))

        return invalid_parameters
Пример #25
0
 def _onchange_units_included_purchase_order_secondary_unit(self):
     if not self.secondary_uom_id:
         return
     factor = self.secondary_uom_id.factor * self.product_id.uom_id.factor
     qty = float_round(
         self.units_included / (factor or 1.0),
         precision_rounding=self.secondary_uom_id.uom_id.rounding)
     if float_compare(
             self.secondary_uom_qty,
             qty,
             precision_rounding=self.secondary_uom_id.uom_id.rounding) != 0:
         self.secondary_uom_qty = qty
Пример #26
0
    def _alipay_form_get_invalid_parameters(self, data):
        invalid_parameters = []

        if float_compare(float(data.get('total_fee', '0.0')), (self.amount + self.fees), 2) != 0:
            invalid_parameters.append(('total_fee', data.get('total_fee'), '%.2f' % (self.amount + self.fees)))  # mc_gross is amount + fees
        if self.acquirer_id.alipay_payment_method == 'standard_checkout':
            if data.get('currency') != self.currency_id.name:
                invalid_parameters.append(('currency', data.get('currency'), self.currency_id.name))
        else:
            if data.get('seller_email') != self.acquirer_id.alipay_seller_email:
                invalid_parameters.append(('seller_email', data.get('seller_email'), self.acquirer_id.alipay_seller_email))
        return invalid_parameters
Пример #27
0
 def action_show_details(self):
     """ Open the produce wizard in order to register tracked components for
     subcontracted product. Otherwise use standard behavior.
     """
     self.ensure_one()
     if self.is_subcontract:
         rounding = self.product_uom.rounding
         production = self.move_orig_ids.production_id[-1:]
         if self._has_tracked_subcontract_components() and\
                 float_compare(production.qty_produced, production.product_uom_qty, precision_rounding=rounding) < 0 and\
                 float_compare(self.quantity_done, self.product_uom_qty, precision_rounding=rounding) < 0:
             return self._action_record_components()
     action = super(StockMove, self).action_show_details()
     if self.is_subcontract and self._has_tracked_subcontract_components():
         action['views'] = [
             (self.env.ref('stock.view_stock_move_operations').id, 'form')
         ]
         action['context'].update({
             'show_lots_m2o': self.has_tracking != 'none',
             'show_lots_text': False,
         })
     return action
Пример #28
0
    def _sips_form_get_invalid_parameters(self, data):
        invalid_parameters = []

        data = self._sips_data_to_object(data.get('Data'))

        # TODO: txn_id: should be false at draft, set afterwards, and verified with txn details
        if self.acquirer_reference and data.get('transactionReference') != self.acquirer_reference:
            invalid_parameters.append(('transactionReference', data.get('transactionReference'), self.acquirer_reference))
        # check what is bought
        if float_compare(float(data.get('amount', '0.0')) / 100, self.amount, 2) != 0:
            invalid_parameters.append(('amount', data.get('amount'), '%.2f' % self.amount))

        return invalid_parameters
Пример #29
0
    def _schedule_hours(self, hours, day_dt, compute_leaves=False, resource_id=None):
        """ Schedule hours of work, using a calendar and an optional resource to
        compute working and leave days. This method can be used backwards, i.e.
        scheduling days before a deadline. For compute_leaves, resource_id:
        see _get_day_work_intervals. This method does not use rrule because
        rrule does not allow backwards computation.

        :param int hours: number of hours to schedule. Use a negative number to
                          compute a backwards scheduling.
        :param datetime day_dt: reference date to compute working days. If days is
                                > 0 date is the starting date. If days is < 0
                                date is the ending date.

        :return list intervals: list of time intervals in naive UTC """
        self.ensure_one()
        backwards = (hours < 0)
        intervals = []
        remaining_hours, iterations = abs(hours * 1.0), 0

        day_dt_tz = to_naive_user_tz(day_dt, self.env.user)
        current_datetime = day_dt_tz

        call_args = dict(compute_leaves=compute_leaves, resource_id=resource_id)

        while float_compare(remaining_hours, 0.0, precision_digits=2) in (1, 0) and iterations < 1000:
            if backwards:
                call_args['end_time'] = current_datetime.time()
            else:
                call_args['start_time'] = current_datetime.time()

            working_intervals = self._get_day_work_intervals(current_datetime.date(), **call_args)

            if working_intervals:
                new_working_intervals = self._interval_schedule_hours(working_intervals, remaining_hours, backwards=backwards)

                res = timedelta()
                for interval in working_intervals:
                    res += interval[1] - interval[0]
                remaining_hours -= res.total_seconds() / 3600.0

                intervals = intervals + new_working_intervals if not backwards else new_working_intervals + intervals
            # get next day
            if backwards:
                current_datetime = datetime.datetime.combine(self._get_previous_work_day(current_datetime), datetime.time(23, 59, 59))
            else:
                current_datetime = datetime.datetime.combine(self._get_next_work_day(current_datetime), datetime.time())
            # avoid infinite loops
            iterations += 1

        return intervals
Пример #30
0
    def _sips_form_get_invalid_parameters(self, data):
        invalid_parameters = []

        data = self._sips_data_to_object(data.get('Data'))

        # amounts should match
        # get currency decimals from const
        sips_currency = SIPS_SUPPORTED_CURRENCIES.get(self.currency_id.name)
        # convert from int to float using decimals from currency
        amount_converted = float(data.get('amount', '0.0')) / (10 ** sips_currency.decimal)
        if float_compare(amount_converted, self.amount, sips_currency.decimal) != 0:
            invalid_parameters.append(('amount', data.get('amount'), '%.2f' % self.amount))

        return invalid_parameters