def trans_rec_get(self): context = self._context or {} credit = debit = 0 lines = self.env['account.move.line'].browse(context.get('active_ids', [])) for line in lines: if not line.full_reconcile_id: credit += line.credit debit += line.debit precision = self.env.user.company_id.currency_id.decimal_places writeoff = float_round(debit - credit, precision_digits=precision) credit = float_round(credit, precision_digits=precision) debit = float_round(debit, precision_digits=precision) return {'trans_nbr': len(lines), 'credit': credit, 'debit': debit, 'writeoff': writeoff}
def _create_stripe_charge(self, acquirer_ref=None, tokenid=None, email=None): api_url_charge = 'https://%s/charges' % ( self.acquirer_id._get_stripe_api_url()) charge_params = { 'amount': int(self.amount if self.currency_id.name in INT_CURRENCIES else float_round(self.amount * 100, 2)), 'currency': self.currency_id.name, 'metadata[reference]': self.reference, 'description': self.reference, } if acquirer_ref: charge_params['customer'] = acquirer_ref if tokenid: charge_params['card'] = str(tokenid) if email: charge_params['receipt_email'] = email.strip() r = requests.post(api_url_charge, auth=(self.acquirer_id.stripe_secret_key, ''), params=charge_params, headers=STRIPE_HEADERS) return r.json()
def get_opening_move_differences(self, opening_move_lines): currency = self.currency_id balancing_move_line = opening_move_lines.filtered( lambda x: x.account_id == self.get_unaffected_earnings_account()) debits_sum = credits_sum = 0.0 for line in opening_move_lines: if line != balancing_move_line: #skip the autobalancing move line debits_sum += line.debit credits_sum += line.credit difference = abs(debits_sum - credits_sum) debit_diff = (debits_sum > credits_sum) and float_round( difference, precision_rounding=currency.rounding) or 0.0 credit_diff = (debits_sum < credits_sum) and float_round( difference, precision_rounding=currency.rounding) or 0.0 return debit_diff, credit_diff
def ogone_form_generate_values(self, values): base_url = self.env['ir.config_parameter'].sudo().get_param( 'web.base.url') ogone_tx_values = dict(values) param_plus = {'return_url': ogone_tx_values.pop('return_url', False)} temp_ogone_tx_values = { 'PSPID': self.ogone_pspid, 'ORDERID': values['reference'], 'AMOUNT': float_repr(float_round(values['amount'], 2) * 100, 0), 'CURRENCY': values['currency'] and values['currency'].name or '', 'LANGUAGE': values.get('partner_lang'), 'CN': values.get('partner_name'), 'EMAIL': values.get('partner_email'), 'OWNERZIP': values.get('partner_zip'), 'OWNERADDRESS': values.get('partner_address'), 'OWNERTOWN': values.get('partner_city'), 'OWNERCTY': values.get('partner_country') and values.get('partner_country').code or '', 'OWNERTELNO': values.get('partner_phone'), 'ACCEPTURL': urls.url_join(base_url, OgoneController._accept_url), 'DECLINEURL': urls.url_join(base_url, OgoneController._decline_url), 'EXCEPTIONURL': urls.url_join(base_url, OgoneController._exception_url), 'CANCELURL': urls.url_join(base_url, OgoneController._cancel_url), 'PARAMPLUS': url_encode(param_plus), } if self.save_token in ['ask', 'always']: temp_ogone_tx_values.update({ 'ALIAS': 'izi-NEW-ALIAS-%s' % time.time(), # something unique, 'ALIASUSAGE': values.get('alias_usage') or self.ogone_alias_usage, }) shasign = self._ogone_generate_shasign('in', temp_ogone_tx_values) temp_ogone_tx_values['SHASIGN'] = shasign ogone_tx_values.update(temp_ogone_tx_values) return ogone_tx_values
def _create_stripe_refund(self): api_url_refund = 'https://%s/refunds' % ( self.acquirer_id._get_stripe_api_url()) refund_params = { 'charge': self.acquirer_reference, 'amount': int( float_round(self.amount * 100, 2) ), # by default, stripe refund the full amount (we don't really need to specify the value) 'metadata[reference]': self.reference, } r = requests.post(api_url_refund, auth=(self.acquirer_id.stripe_secret_key, ''), params=refund_params, headers=STRIPE_HEADERS) return r.json()
def value_to_html(self, value, options): if 'decimal_precision' in options: precision = self.env['decimal.precision'].search([('name', '=', options['decimal_precision'])]).digits else: precision = options['precision'] if precision is None: fmt = '%f' else: value = float_utils.float_round(value, precision_digits=precision) fmt = '%.{precision}f'.format(precision=precision) formatted = self.user_lang().format(fmt, value, grouping=True).replace(r'-', u'\u2011') # %f does not strip trailing zeroes. %g does but its precision causes # it to switch to scientific notation starting at a million *and* to # strip decimals. So use %f and if no precision was specified manually # strip trailing 0. if precision is None: formatted = re.sub(r'(?:(0|\d+?)0+)$', r'\1', formatted) return pycompat.to_text(formatted)
def default_get(self, fields): if len(self.env.context.get('active_ids', list())) > 1: raise UserError("You may only return one picking at a time!") res = super(ReturnPicking, self).default_get(fields) move_dest_exists = False product_return_moves = [] picking = self.env['stock.picking'].browse(self.env.context.get('active_id')) if picking: res.update({'picking_id': picking.id}) if picking.state != 'done': raise UserError(_("You may only return Done pickings")) for move in picking.move_lines: if move.scrapped: continue if move.move_dest_ids: move_dest_exists = True quantity = move.product_qty - sum(move.move_dest_ids.filtered(lambda m: m.state in ['partially_available', 'assigned', 'done']).\ mapped('move_line_ids').mapped('product_qty')) quantity = float_round(quantity, precision_rounding=move.product_uom.rounding) product_return_moves.append((0, 0, {'product_id': move.product_id.id, 'quantity': quantity, 'move_id': move.id, 'uom_id': move.product_id.uom_id.id})) if not product_return_moves: raise UserError(_("No products to return (only lines in Done state and not fully returned yet can be returned)!")) if 'product_return_moves' in fields: res.update({'product_return_moves': product_return_moves}) if 'move_dest_exists' in fields: res.update({'move_dest_exists': move_dest_exists}) if 'parent_location_id' in fields and picking.location_id.usage == 'internal': res.update({'parent_location_id': picking.picking_type_id.warehouse_id and picking.picking_type_id.warehouse_id.view_location_id.id or picking.location_id.location_id.id}) if 'original_location_id' in fields: res.update({'original_location_id': picking.location_id.id}) if 'location_id' in fields: location_id = picking.location_id.id if picking.picking_type_id.return_picking_type_id.default_location_dest_id.return_location: location_id = picking.picking_type_id.return_picking_type_id.default_location_dest_id.id res['location_id'] = location_id return res
def _compute_quantities_dict(self, lot_id, owner_id, package_id, from_date=False, to_date=False): domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self._get_domain_locations( ) domain_quant = [('product_id', 'in', self.ids)] + domain_quant_loc dates_in_the_past = False if to_date and to_date < fields.Datetime.now( ): #Only to_date as to_date will correspond to qty_available dates_in_the_past = True domain_move_in = [('product_id', 'in', self.ids)] + domain_move_in_loc domain_move_out = [('product_id', 'in', self.ids) ] + domain_move_out_loc if lot_id: domain_quant += [('lot_id', '=', lot_id)] if owner_id: domain_quant += [('owner_id', '=', owner_id)] domain_move_in += [('restrict_partner_id', '=', owner_id)] domain_move_out += [('restrict_partner_id', '=', owner_id)] if package_id: domain_quant += [('package_id', '=', package_id)] if dates_in_the_past: domain_move_in_done = list(domain_move_in) domain_move_out_done = list(domain_move_out) if from_date: domain_move_in += [('date', '>=', from_date)] domain_move_out += [('date', '>=', from_date)] if to_date: domain_move_in += [('date', '<=', to_date)] domain_move_out += [('date', '<=', to_date)] Move = self.env['stock.move'] Quant = self.env['stock.quant'] domain_move_in_todo = [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_move_in domain_move_out_todo = [ ('state', 'not in', ('done', 'cancel', 'draft')) ] + domain_move_out moves_in_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group( domain_move_in_todo, ['product_id', 'product_qty'], ['product_id'], orderby='id')) moves_out_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group( domain_move_out_todo, ['product_id', 'product_qty'], ['product_id'], orderby='id')) quants_res = dict( (item['product_id'][0], item['quantity']) for item in Quant.read_group( domain_quant, ['product_id', 'quantity'], ['product_id'], orderby='id')) if dates_in_the_past: # Calculate the moves that were done before now to calculate back in time (as most questions will be recent ones) domain_move_in_done = [('state', '=', 'done'), ('date', '>', to_date) ] + domain_move_in_done domain_move_out_done = [('state', '=', 'done'), ('date', '>', to_date) ] + domain_move_out_done moves_in_res_past = dict( (item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_in_done, ['product_id', 'product_qty'], ['product_id'], orderby='id')) moves_out_res_past = dict( (item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_out_done, ['product_id', 'product_qty'], ['product_id'], orderby='id')) res = dict() for product in self.with_context(prefetch_fields=False): res[product.id] = {} if dates_in_the_past: qty_available = quants_res.get( product.id, 0.0) - moves_in_res_past.get( product.id, 0.0) + moves_out_res_past.get( product.id, 0.0) else: qty_available = quants_res.get(product.id, 0.0) res[product.id]['qty_available'] = float_round( qty_available, precision_rounding=product.uom_id.rounding) res[product.id]['incoming_qty'] = float_round( moves_in_res.get(product.id, 0.0), precision_rounding=product.uom_id.rounding) res[product.id]['outgoing_qty'] = float_round( moves_out_res.get(product.id, 0.0), precision_rounding=product.uom_id.rounding) res[product.id]['virtual_available'] = float_round( qty_available + res[product.id]['incoming_qty'] - res[product.id]['outgoing_qty'], precision_rounding=product.uom_id.rounding) return res
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()
def _action_done(self): """ This method is called during a move's `action_done`. It'll actually move a quant from the source location to the destination location, and unreserve if needed in the source location. This method is intended to be called on all the move lines of a move. This method is not intended to be called when editing a `done` move (that's what the override of `write` here is done. """ # First, we loop over all the move lines to do a preliminary check: `qty_done` should not # be negative and, according to the presence of a picking type or a linked inventory # adjustment, enforce some rules on the `lot_id` field. If `qty_done` is null, we unlink # the line. It is mandatory in order to free the reservation and correctly apply # `action_done` on the next move lines. ml_to_delete = self.env['stock.move.line'] for ml in self: # Check here if `ml.qty_done` respects the rounding of `ml.product_uom_id`. uom_qty = float_round( ml.qty_done, precision_rounding=ml.product_uom_id.rounding, rounding_method='HALF-UP') precision_digits = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') qty_done = float_round(ml.qty_done, precision_digits=precision_digits, rounding_method='HALF-UP') if float_compare( uom_qty, qty_done, precision_digits=precision_digits) != 0: raise UserError( _('The quantity done for the product "%s" doesn\'t respect the rounding precision \ defined on the unit of measure "%s". Please change the quantity done or the \ rounding precision of your unit of measure.') % (ml.product_id.display_name, ml.product_uom_id.name)) qty_done_float_compared = float_compare( ml.qty_done, 0, precision_rounding=ml.product_uom_id.rounding) if qty_done_float_compared > 0: if ml.product_id.tracking != 'none': picking_type_id = ml.move_id.picking_type_id if picking_type_id: if picking_type_id.use_create_lots: # If a picking type is linked, we may have to create a production lot on # the fly before assigning it to the move line if the user checked both # `use_create_lots` and `use_existing_lots`. if ml.lot_name and not ml.lot_id: lot = self.env['stock.production.lot'].create({ 'name': ml.lot_name, 'product_id': ml.product_id.id }) ml.write({'lot_id': lot.id}) elif not picking_type_id.use_create_lots and not picking_type_id.use_existing_lots: # If the user disabled both `use_create_lots` and `use_existing_lots` # checkboxes on the picking type, he's allowed to enter tracked # products without a `lot_id`. continue elif ml.move_id.inventory_id: # If an inventory adjustment is linked, the user is allowed to enter # tracked products without a `lot_id`. continue if not ml.lot_id: raise UserError( _('You need to supply a lot/serial number for %s.') % ml.product_id.name) elif qty_done_float_compared < 0: raise UserError(_('No negative quantities allowed')) else: ml_to_delete |= ml ml_to_delete.unlink() # Now, we can actually move the quant. done_ml = self.env['stock.move.line'] for ml in self - ml_to_delete: if ml.product_id.type == 'product': Quant = self.env['stock.quant'] rounding = ml.product_uom_id.rounding # if this move line is force assigned, unreserve elsewhere if needed if not ml.location_id.should_bypass_reservation( ) and float_compare(ml.qty_done, ml.product_qty, precision_rounding=rounding) > 0: extra_qty = ml.qty_done - ml.product_qty ml._free_reservation(ml.product_id, ml.location_id, extra_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, ml_to_ignore=done_ml) # unreserve what's been reserved if not ml.location_id.should_bypass_reservation( ) and ml.product_id.type == 'product' and ml.product_qty: try: Quant._update_reserved_quantity( ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, strict=True) except UserError: Quant._update_reserved_quantity( ml.product_id, ml.location_id, -ml.product_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True) # move what's been actually done quantity = ml.product_uom_id._compute_quantity( ml.qty_done, ml.move_id.product_id.uom_id, rounding_method='HALF-UP') available_qty, in_date = Quant._update_available_quantity( ml.product_id, ml.location_id, -quantity, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) if available_qty < 0 and ml.lot_id: # see if we can compensate the negative quants with some untracked quants untracked_qty = Quant._get_available_quantity( ml.product_id, ml.location_id, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True) if untracked_qty: taken_from_untracked_qty = min(untracked_qty, abs(quantity)) Quant._update_available_quantity( ml.product_id, ml.location_id, -taken_from_untracked_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id) Quant._update_available_quantity( ml.product_id, ml.location_id, taken_from_untracked_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id) Quant._update_available_quantity( ml.product_id, ml.location_dest_id, quantity, lot_id=ml.lot_id, package_id=ml.result_package_id, owner_id=ml.owner_id, in_date=in_date) done_ml |= ml # Reset the reserved quantity as we just moved it to the destination location. (self - ml_to_delete).with_context(bypass_reservation_update=True).write({ 'product_uom_qty': 0.00, 'date': fields.Datetime.now(), })