Odoo - Confirm Sales Order - python

Hi I am using Odoo10 and trying to create a sales order in POS, below code creates the Sales orders quotation. I want to Confirm the Sale and create Sales order not quotation.
#api.model
def create_sales_order(self, orderline, customer_id, sign):
sale_pool = self.env['sale.order']
prod_pool = self.env['product.product']
sale_line_pool = self.env['sale.order.line']
sale_no = ''
sale = {}
if customer_id:
customer_id = int(customer_id)
sale = {'partner_id': customer_id,
'partner_invoice_id': customer_id,
'partner_shipping_id': customer_id,
'signature': sign}
sale_id = sale_pool.create(sale)
if sale_id:
sale_brw = sale_id
sale_brw.onchange_partner_id()
#create sale order line
for line in orderline:
sale_line = {}
if line.get('product_id'):
prod_rec = prod_pool.browse(line['product_id'])
sale_line.update({'name': prod_rec.name or False,
'product_id': prod_rec.id,
'product_uom_qty': line['qty'],
'discount': line.get('discount'),
'order_id': sale_id.id})
sale_line_id = sale_line_pool.create(sale_line)
for line in sale_line_id:
line.product_id_change()
return {"name": sale_brw.name, "id": sale_brw.id }
How do I create sales order not quotation?

Short answer: Set state to "sale":
sale = {'partner_id': customer_id,
'partner_invoice_id': customer_id,
'partner_shipping_id': customer_id,
'signature': sign,
'state': 'sale'}
Sale orders and Quotations are saved on the same model (namely, sale.order) you can tell whether it is a SO or a Quote by looking at its state:
state | Meaning
-------|--------
draft | Quotation
sent | Quotation Sent
sale | Sales Order
done | Locked
cancel | Cancelled
Also, you can look at the function action_confirm which is triggered by clicking on the Confirm Sale button on addons/sale/models/sale.py file:
445 def action_done(self):
446 return self.write({'state': 'done'})
...
451 #api.multi
452 def action_confirm(self):
453 for order in self:
454 order.state = 'sale'
455 order.confirmation_date = fields.Datetime.now()
456 if self.env.context.get('send_email'):
457 self.force_quotation_send()
458 order.order_line._action_procurement_create()
459 if self.env['ir.values'].get_default('sale.config.settings', 'auto_done_setting'):
460 self.action_done()
461 return True

Related

How to send data from sale order line to invoice line in Odoo v12?

How to send data from custom field in sale order line to another custom field in the invoice order line in Odoo 12? Which method should I edit?
I used these methods but dose not work with me
class sale_advance_payment_inherit(models.TransientModel):
_inherit = 'sale.advance.payment.inv'
#api.multi
def _create_invoice(self, order, so_line, amount):
inv_obj = self.env['account.invoice']
ir_property_obj = self.env['ir.property']
account_id = False
if self.product_id.id:
account_id = self.product_id.property_account_income_id.id or self.product_id.categ_id.property_account_income_categ_id.id
if not account_id:
inc_acc = ir_property_obj.get('property_account_income_categ_id', 'product.category')
account_id = order.fiscal_position_id.map_account(inc_acc).id if inc_acc else False
if not account_id:
raise UserError(
_(
'There is no income account defined for this product: "%s". You may have to install a chart of account from Accounting app, settings menu.') %
(self.product_id.name,))
if self.amount <= 0.00:
raise UserError(_('The value of the down payment amount must be positive.'))
context = {'lang': order.partner_id.lang}
if self.advance_payment_method == 'percentage':
amount = order.amount_untaxed * self.amount / 100
name = _("Down payment of %s%%") % (self.amount,)
else:
amount = self.amount
name = _('Down Payment')
del context
taxes = self.product_id.taxes_id.filtered(lambda r: not order.company_id or r.company_id == order.company_id)
if order.fiscal_position_id and taxes:
tax_ids = order.fiscal_position_id.map_tax(taxes, self.product_id, order.partner_shipping_id).ids
else:
tax_ids = taxes.ids
print ('fffffffffffffffffff')
invoice = inv_obj.create({
'name': order.client_order_ref or order.name,
'origin': order.name,
'type': 'out_invoice',
'reference': False,
'account_id': order.partner_id.property_account_receivable_id.id,
'partner_id': order.partner_invoice_id.id,
'partner_shipping_id': order.partner_shipping_id.id,
'invoice_line_ids': [(0, 0, {
'name': name,
# 'tracking': so_line.tracking,
# 'receiver_or_sender_customer': self.receiver_or_sender_customer,
# 'courier_id': self.courier_id.id,
# 'from_zone_id': self.from_zone_id.id,
# 'to_zone_id': self.to_zone_id.id,
# 'wight': self.wight,
# 'wight_category_id': self.wight_category_id.id,
# 'extra_wight_fees': self.extra_wight_fees,
# 'cod': self.cod,
# 'cod_fees': self.cod_fees,
'origin': order.name,
'account_id': account_id,
'price_unit': amount,
'quantity': 1.0,
'discount': 0.0,
'uom_id': self.product_id.uom_id.id,
'product_id': self.product_id.id,
'sale_line_ids': [(6, 0, [so_line.id])],
'invoice_line_tax_ids': [(6, 0, tax_ids)],
'analytic_tag_ids': [(6, 0, so_line.analytic_tag_ids.ids)],
'account_analytic_id': order.analytic_account_id.id or False,
})],
'currency_id': order.pricelist_id.currency_id.id,
'payment_term_id': order.payment_term_id.id,
'fiscal_position_id': order.fiscal_position_id.id or order.partner_id.property_account_position_id.id,
'team_id': order.team_id.id,
'user_id': order.user_id.id,
'comment': order.note,
})
invoice.compute_taxes()
invoice.message_post_with_view('mail.message_origin_link',
values={'self': invoice, 'origin': order},
subtype_id=self.env.ref('mail.mt_note').id)
return invoice
#api.multi
def create_invoices(self):
sale_orders = self.env['sale.order'].browse(self._context.get('active_ids', []))
if self.advance_payment_method == 'delivered':
sale_orders.action_invoice_create()
elif self.advance_payment_method == 'all':
sale_orders.action_invoice_create(final=True)
else:
# Create deposit product if necessary
if not self.product_id:
vals = self._prepare_deposit_product()
self.product_id = self.env['product.product'].create(vals)
self.env['ir.config_parameter'].sudo().set_param('sale.default_deposit_product_id', self.product_id.id)
sale_line_obj = self.env['sale.order.line']
for order in sale_orders:
if self.advance_payment_method == 'percentage':
amount = order.amount_untaxed * self.amount / 100
else:
amount = self.amount
if self.product_id.invoice_policy != 'order':
raise UserError(_(
'The product used to invoice a down payment should have an invoice policy set to "Ordered quantities". Please update your deposit product to be able to create a deposit invoice.'))
if self.product_id.type != 'service':
raise UserError(_(
"The product used to invoice a down payment should be of type 'Service'. Please use another product or update this product."))
taxes = self.product_id.taxes_id.filtered(
lambda r: not order.company_id or r.company_id == order.company_id)
if order.fiscal_position_id and taxes:
tax_ids = order.fiscal_position_id.map_tax(taxes, self.product_id, order.partner_shipping_id).ids
else:
print ('assssssssssssssss')
tax_ids = taxes.ids
context = {'lang': order.partner_id.lang}
analytic_tag_ids = []
for line in order.order_line:
analytic_tag_ids = [(4, analytic_tag.id, None) for analytic_tag in line.analytic_tag_ids]
tracking = line.tracking
so_line = sale_line_obj.create({
'name': _('Advance: %s') % (time.strftime('%m %Y'),),
'price_unit': amount,
'product_uom_qty': 0.0,
'order_id': order.id,
'discount': 0.0,
# 'tracking': tracking,
'product_uom': self.product_id.uom_id.id,
'product_id': self.product_id.id,
'analytic_tag_ids': analytic_tag_ids,
'tax_id': [(6, 0, tax_ids)],
'is_downpayment': True,
})
del context
self._create_invoice(order, so_line, amount)
if self._context.get('open_invoices', False):
return sale_orders.action_view_invoice()
return {'type': 'ir.actions.act_window_close'}
You can inherit from this method here:
#api.multi
def invoice_line_create(self, invoice_id, qty):
""" Create an invoice line. The quantity to invoice can be positive (invoice) or negative (refund).
.. deprecated:: 12.0
Replaced by :func:`invoice_line_create_vals` which can be used for creating
`account.invoice.line` records in batch
:param invoice_id: integer
:param qty: float quantity to invoice
:returns recordset of account.invoice.line created
"""
return self.env['account.invoice.line'].create(
self.invoice_line_create_vals(invoice_id, qty))
So, make something like this:
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
#api.multi
def invoice_line_create(self, invoice_id, qty):
invoice_lines = super().invoice_line_create(invoice_id, qty)
for invoice_line in invoice_lines:
for sale_line in invoice_line.sale_line_ids:
for move_line in sale_line.move_ids:
invoice_line.write({
'new_field': move_line.new_field,
})
return invoice_lines

inventory question regarding adding and removing product using dictionary

I am working on task like this, in a company, they want to sale product called 'computer'.
if enough stock does not exists before the sale date, it should give message for insufficient qty , message.
Also should give information of total available product quantity for sale before that date, and it's valuation.
if company have enough quantity for sale, than it should display message of remaining qty of that product and it's valuation.
e.g.
valuation = quantity * purchase price
my code
import datetime
class Supplier:
def add_supplier(self, name, address, email, contact_no):
"""
Function: Used to add supplier details.
Parameter:name: Supplier Name
address: Supplier address
email: Supplier email
contact_no: Supplier Contact detail
Return: Nothing
"""
self.name = name
self.address = address
self.email = email
self.contact_no = contact_no
class Product:
def add_product(self, name):
"""
Function: To add product in to an inventory.
Parameter: Product name
Return: Nothing
"""
self.name = name
class Company(Supplier, Product):
data_dict = []
def purchase(self, product_obj, qty, price, date=datetime.date.today()):
"""
Function: Used to purchase product.
Parameter: product_obj: Product object.
qty: Product quantity.
price: Product price.
date: Product date.
Return: Nothing
"""
self.data_dict.append({'product': product_obj.name, 'qty': qty, 'price': price, 'date': str(date)})
def sale(self, sale_qty, sale_date):
"""
Function: To sale product from warehouse.
Parameter: sale_qty: Product sell quantity.
sale_date: Product sell date.
Return: Nothing
"""
self.newdict = (sorted(self.data_dict, key=lambda x: x['date']))
assert sale_qty > 0
unit_val = 0
rqilr = 0
rpilr = 0
qty = 0
price = 0
sum_qty = 0
sum_price = 0
#print(newdict) # testing.
for i, row in enumerate(self.newdict):
if row['date'] > sale_date:
print("Sorry, not enough quantity.\nStart the purchase order procedure pls!\n")
qty += row['qty']
price += row['price']
unit_val = price/qty
# we have enough Computers in stock.
if qty >= sale_qty:
break
else: # loop completes normally
for i in self.newdict:
sum_price += i['price']
sum_qty += i['qty']
print("Sorry, not enough qty. Operation cancelled.\nCurrent quantity = {} and valuation = {}".format(sum_qty, sum_price))
# remaining_qty_in_last_row sort name = rqilr
rqilr = qty - sale_qty
rpilr = rqilr * unit_val
self.newdict = [{**row, 'price': rpilr, 'qty': rqilr}] + self.newdict[i + 1:]
#print(self.newdict)
# For current quantity and valuation
for i in self.newdict:
sum_price += i['price']
sum_qty += i['qty']
print("Sold!\n available quantity = {} and valuation = {}".format(sum_qty, sum_price))
C = Company()
PRODUCT_OBJ = Product()
PRODUCT_OBJ.add_product('Computer')
while True:
option = int(input("""1. You want to add stock of the product!"
2. Want to sale product?
"""))
if option == 1:
qty = int(input("Enter the qty of the product.\n"))
price = float(input("Enter the price of the product.\n"))
purchase_date = input("Enter purchase date.\n")
C.purchase(PRODUCT_OBJ, qty, price, purchase_date)
elif option == 2:
qty = int(input("Enter the qty you wanna sale, pal!"))
sale_date = input("Enter sale date.\n")
C.sale(qty, sale_date)
else:
print("Enter only 1 & 2 option.\n")
after executing program I am having menu like in program.
You want to add stock of the product!
Want to sale product?
but output not getting proper output of remaining quantity and valuation.
Any suggestion , how to make it right?

Adapting odoo report for email - How to change env for current partner_id only

I have a odoo report from the OCA that I am trying to adapt to be able to be sent as an email to individual customers. The report is seen here Report and Github link
The below is the important part (as I see it). In this function 'data' is passed through from a wizard.
#api.multi
def render_html(self, docids, data):
company_id = data['company_id']
partner_ids = data['partner_ids']
date_start = data['date_start']
date_end = data['date_end']
today = fields.Date.today()
balance_start_to_display, buckets_to_display = {}, {}
lines_to_display, amount_due = {}, {}
currency_to_display = {}
today_display, date_start_display, date_end_display = {}, {}, {}
balance_start = self._get_account_initial_balance(
company_id, partner_ids, date_start)
When I attempt to add the report to an email template as an attachement, I do not know how to pass through parameters to it as well. So I did the following:
If Data is None...
data={
'date_start': str(date.today()-timedelta(days=120)),
'date_end': str(date.today()),
'company_id': self.env.user.company_id.id,
'partner_ids': self._context['active_ids'],
'show_aging_buckets': True,
'filter_non_due_partners': True,
}`
The problem is ' 'partner_ids': self._context['active_ids'], '
Returns every customers statement on the email to every customer. How do I make it so this is only for the current customer?
Thanks in advance for any assistance.

how to calculate monthly portfolio shares and dividends

I have a simple app that should track users stock portfolio. Basicly user may buy stocks like JNJ, AAPL or MCD. He can also sell some/all of them. Dividends might be reinvested instantly as they are paid out (same as if user did buy same stock for it's dividend value). I need to calculate this portfolio value on monthly basis.
Easy example:
Transactions:
+----------+--------+------------+-------+
| buy/sell | amount | date | price |
+----------+--------+------------+-------+
| buy | 5 | 2015-01-01 | $60 |
| sell | 1 | 2015-03-01 | $70 |
+----------+--------+------------+-------+
From this transactions I would like to get this dictionary of shares:
{
u'JNJ': {
datetime.date(2015, 6, 1): Decimal('5.00000'),
datetime.date(2015, 7, 1): Decimal('5.00000'),
datetime.date(2015, 8, 1): Decimal('4.00000'),
datetime.date(2015, 9, 1): Decimal('4.00000'),
datetime.date(2015, 10, 1): Decimal('4.00000')}
}
These are my shares by month. Lets say there was a $0.75 dividend on 2015-08-21 and it on same day, I bought partial shares of JNJ on this date:
Example with Dividends:
Transactions:
+----------+--------+------------+-------+
| buy/sell | amount | date | price |
+----------+--------+------------+-------+
| buy | 5 | 2015-01-01 | $60 |
| sell | 1 | 2015-03-01 | $70 |
+----------+--------+------------+-------+
Dividends:
+------------+--------+-------+
| date | amount | price |
+------------+--------+-------+
| 2015-08-21 | 0.75 | 64 |
+------------+--------+-------+
When dividend was paid, I was holding 4 shares. For 4 shares I received 4*$0.75 and I bought 0.031393889 shares of JNJ.
Result:
{u'JNJ':
{
datetime.date(2015, 6, 1): Decimal('5.00000'),
datetime.date(2015, 7, 1): Decimal('5.00000'),
datetime.date(2015, 8, 1): Decimal('4.031393889'),
datetime.date(2015, 9, 1): Decimal('4.031393889'),
datetime.date(2015, 10, 1): Decimal('4.031393889')}
}
So this is what I have to calculate. There might be any number of transaction and dividends. There must be at least one Buy transaction, but dividends may not exist.
These are my classes in models.py:
Stock model representing Stock, for example JNJ.
class Stock(models.Model):
name = models.CharField("Stock's name", max_length=200, default="")
symbol = models.CharField("Stock's symbol", max_length=20, default="", db_index=True)
price = models.DecimalField(max_digits=30, decimal_places=5, null=True, blank=True)
Than I have StockTransaction, which represends object for one stock for one portfolio. Transactions are linked to StockTransaction, because drip applies to all Transactions.
class StockTransaction(models.Model):
stock = models.ForeignKey('stocks.Stock')
portfolio = models.ForeignKey(Portfolio, related_name="stock_transactions")
drip = models.BooleanField(default=False)
Transaction class:
BUYCHOICE = [(True,'Buy'),(False,'Sell')]
class Transaction(models.Model):
amount = models.DecimalField(max_digits=20, decimal_places=5, validators=[MinValueValidator(Decimal('0.0001'))])
buy = models.BooleanField(choices=BUYCHOICE, default=True)
date = models.DateField('buy date')
price = models.DecimalField('price per share', max_digits=20, decimal_places=5, validators=[MinValueValidator(Decimal('0.0001'))])
stock_transaction = models.ForeignKey(StockTransaction, related_name="transactions", null=False)
and lastly Dividend class:
class Dividend(models.Model):
date = models.DateField('pay date', db_index=True)
amount = models.DecimalField(max_digits=20, decimal_places=10)
price = models.DecimalField('price per share', max_digits=20, decimal_places=10)
stock_transaction = models.ManyToManyField('portfolio.StockTransaction', related_name="dividends", blank=True)
stock = models.ForeignKey(Stock, related_name="dividends")
I've coded my method, but I do think there is a better way. My method is too long and takes to much time for portfolio with 106 stocks (each 5 transactions). Here is my method:
def get_portfolio_month_shares(portfolio_id):
"""
Return number of dividends and shares per month respectfully in dict
{symbol: {year: decimal, year: decimal} }
:param portfolio: portfolio object for which to calculate shares and dividends
:return: total dividends and amount of shares, respectfully
"""
total_shares, total_dividends = {}, {}
for stock_transaction in StockTransaction.objects.filter(portfolio_id=portfolio_id)\
.select_related('stock').prefetch_related('dividends', 'transactions', 'stock__dividends'):
shares = 0 #number of shares
monthly_shares, monthly_dividends = {}, {}
transactions = list(stock_transaction.transactions.all())
first_transaction = transactions[0]
for dividend in stock_transaction.stock.dividends.all():
if dividend.date < first_transaction.date:
continue
try:
#transactions that are older than last dividend
while transactions[0].date < dividend.date:
if transactions[0].buy:
shares = shares + transactions[0].amount
else: #transaction is a sell
shares = shares - transactions[0].amount
monthly_shares[date(transactions[0].date.year, transactions[0].date.month, 1)] = shares
transactions.remove(transactions[0])
except IndexError: #no more transactions
pass
if dividend in stock_transaction.dividends.all(): # if drip is active for dividend
if dividend.price!=0:
shares += (dividend.amount * shares / dividend.price)
monthly_shares[date(dividend.date.year, dividend.date.month, 1)] = shares
try:
monthly_dividends[date(dividend.date.year, dividend.date.month, 1)] += shares * dividend.amount
except KeyError:
monthly_dividends[date(dividend.date.year, dividend.date.month, 1)] = shares * dividend.amount
#fill blank months with 0
if monthly_shares!={}:
for dt in rrule.rrule(rrule.MONTHLY,
dtstart=first_transaction.date,
until=datetime.now() + relativedelta.relativedelta(months=1)):
try:
monthly_shares[date(dt.year, dt.month, 1)]
except KeyError: #keyerror on dt year
dt_previous = dt - relativedelta.relativedelta(months=1)
monthly_shares[date(dt.year, dt.month, 1)] = monthly_shares[date(dt_previous.year, dt_previous.month, 1)]
try:
monthly_dividends[date(dt.year, dt.month, 1)]
except KeyError:
monthly_dividends[date(dt.year, dt.month, 1)] = 0
# for each transaction not covered by dividend for cycle
if transactions:
for transaction in transactions:
for dt in rrule.rrule(rrule.MONTHLY,
dtstart=transaction.date,
until=datetime.now() + relativedelta.relativedelta(months=1)):
if transaction.buy:
try:
monthly_shares[date(dt.year, dt.month, 1)] += transaction.amount
except KeyError:
monthly_shares[date(dt.year, dt.month, 1)] = transaction.amount
else: #sell
monthly_shares[date(dt.year, dt.month, 1)] -= transaction.amount
total_dividends[stock_transaction.stock.symbol] = monthly_dividends
total_shares[stock_transaction.stock.symbol] = monthly_shares
return total_dividends, total_shares
Description:
First for cycle - for each stock in portfolio.
Second for cycle - for each stock's dividend
this line if dividend in stock_transaction.dividends.all() checks if dividends are reinvested. There exists m2m relation between stock_transaction and dividend objects if they are.
for cycle with rrule fills up blank months to previous month value.
EDIT1:
I already optimized number of sql queries with django-debug-toolbar (4 sql queries needed). My code is slow probably because of many objects and large dictionaries.
Just a shot in the dark here (I'm not familiar with stock dividends, so I can't comment on the math).
It looks like this could be your bottleneck:
for dividend in stock_transaction.stock.dividends.all():
You select_related on stock and you prefetch_related on dividends, but you don't grab stock__dividends. You can check whether or not this is the bottleneck using the Django Debug Toolbar.
If this repeating query is the root problem, then you may try adding it in:
...select_related('stock', 'stock__dividends')...

OpenERP Unique Constraint

I have a table in OpenERP/PostgreSQL with the following columns: name and description.
I added the following validation for unique name:
_sql_constraints = [('unique_name', 'unique(name)', 'A record with the same name already exists.')]
It works fine but it is case sensitive. Currently, it accepts values such as "Mickey", "MICKEY" and "mickey":
Wrong Way:
--------------------------
| name | description |
--------------------------
| mickey | not a mouse |
--------------------------
| MICKEY | not a mouse |
--------------------------
| Mickey | not a mouse |
--------------------------
Is there a way to revise the validation code so that it will not allow users to add several values such as "Mickey", "MICKEY" and "mickey"? How can I make the unique key validation case insensitive?
Right Way:
--------------------------------
| name | description |
--------------------------------
| mickey | not a mouse |
--------------------------------
| mickey mouse | is a mouse |
--------------------------------
| donald | is a duck |
--------------------------------
For case insensitive constraints check out HERE
else you can always use Openerp Constraints instead of SQL .
for openerp Constraints
check the example
def _check_unique_insesitive(self, cr, uid, ids, context=None):
sr_ids = self.search(cr, 1 ,[], context=context)
lst = [
x.FIELD.lower() for x in self.browse(cr, uid, sr_ids, context=context)
if x.FIELD and x.id not in ids
]
for self_obj in self.browse(cr, uid, ids, context=context):
if self_obj.FILD and self_obj.FILD.lower() in lst:
return False
return True
_constraints = [(_check_unique_insesitive, 'Error: UNIQUE MSG', ['FIELD'])]
This way without read all data from database:
def _check_unique_insesitive(self, cr, uid, ids, context=None):
for self_obj in self.browse(cr, uid, ids, context=context):
if self_obj.name and self.search_count(cr, uid, [('name', '=ilike', self_obj.name), ('id', '!=', self_obj.id)], context=context) != 0:
return False
return True
_constraints = [(_check_unique_insesitive, _('The name must be unique!'), ['name'])]
using constrains in Odoo 8.0 or above in a simpler way.
get all records of the model and check the desired field value with lower() and excluding the self record.
#api.constrains('code')
def _check_duplicate_code(self):
codes = self.search([])
for c in codes:
if self.code.lower() == c.code.lower() and self.id != c.id:
raise exceptions.ValidationError("Error: code must be unique")

Categories

Resources