[MOV] purchase: split code in right file to fit guidelines
No code changes, simply move them to the right places, the place where they should be. This will ease the split between stock and purchase addons.
This commit is contained in:
@@ -19,8 +19,10 @@
|
||||
'views/purchase_views.xml',
|
||||
'views/stock_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/product_views.xml',
|
||||
'views/res_partner_views.xml',
|
||||
'views/purchase_template.xml',
|
||||
'report/purchase_bill_views.xml',
|
||||
'report/purchase_report_views.xml',
|
||||
'data/mail_template_data.xml',
|
||||
'views/portal_templates.xml',
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
from . import account_invoice
|
||||
from . import purchase
|
||||
from . import purchase_bill
|
||||
from . import procurement
|
||||
from . import product
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import res_partner
|
||||
|
||||
@@ -7,12 +7,26 @@ from odoo.tools.float_utils import float_compare
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
purchase_id = fields.Many2one(
|
||||
comodel_name='purchase.order',
|
||||
string='Add Purchase Order',
|
||||
readonly=True, states={'draft': [('readonly', False)]},
|
||||
help='Load the vendor bill based on selected purchase order. Several PO can be selected.'
|
||||
)
|
||||
vendor_bill_purchase_id = fields.Many2one(
|
||||
comodel_name='purchase.bill.union',
|
||||
string='Auto-Complete'
|
||||
)
|
||||
|
||||
@api.onchange('vendor_bill_purchase_id')
|
||||
def _onchange_bill_purchase_order(self):
|
||||
if not self.vendor_bill_purchase_id:
|
||||
return {}
|
||||
self.purchase_id = self.vendor_bill_purchase_id.purchase_order_id
|
||||
self.vendor_bill_id = self.vendor_bill_purchase_id.vendor_bill_id
|
||||
self.vendor_bill_purchase_id = False
|
||||
return {}
|
||||
|
||||
@api.onchange('state', 'partner_id', 'invoice_line_ids')
|
||||
def _onchange_allowed_purchase_ids(self):
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ProcurementRule(models.Model):
|
||||
_inherit = 'procurement.rule'
|
||||
|
||||
action = fields.Selection(selection_add=[('buy', 'Buy')])
|
||||
|
||||
@api.multi
|
||||
def _run_buy(self, product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
cache = {}
|
||||
suppliers = product_id.seller_ids\
|
||||
.filtered(lambda r: (not r.company_id or r.company_id == values['company_id']) and (not r.product_id or r.product_id == product_id))
|
||||
if not suppliers:
|
||||
msg = _('There is no vendor associated to the product %s. Please define a vendor for this product.') % (product_id.display_name,)
|
||||
raise UserError(msg)
|
||||
|
||||
supplier = self._make_po_select_supplier(values, suppliers)
|
||||
partner = supplier.name
|
||||
# we put `supplier_info` in values for extensibility purposes
|
||||
values['supplier'] = supplier
|
||||
|
||||
domain = self._make_po_get_domain(values, partner)
|
||||
if domain in cache:
|
||||
po = cache[domain]
|
||||
else:
|
||||
po = self.env['purchase.order'].search([dom for dom in domain])
|
||||
po = po[0] if po else False
|
||||
cache[domain] = po
|
||||
if not po:
|
||||
vals = self._prepare_purchase_order(product_id, product_qty, product_uom, origin, values, partner)
|
||||
po = self.env['purchase.order'].create(vals)
|
||||
cache[domain] = po
|
||||
elif not po.origin or origin not in po.origin.split(', '):
|
||||
if po.origin:
|
||||
if origin:
|
||||
po.write({'origin': po.origin + ', ' + origin})
|
||||
else:
|
||||
po.write({'origin': po.origin})
|
||||
else:
|
||||
po.write({'origin': origin})
|
||||
|
||||
# Create Line
|
||||
po_line = False
|
||||
for line in po.order_line:
|
||||
if line.product_id == product_id and line.product_uom == product_id.uom_po_id:
|
||||
if line._merge_in_existing_line(product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
vals = self._update_purchase_order_line(product_id, product_qty, product_uom, values, line, partner)
|
||||
po_line = line.write(vals)
|
||||
break
|
||||
if not po_line:
|
||||
vals = self._prepare_purchase_order_line(product_id, product_qty, product_uom, values, po, partner)
|
||||
self.env['purchase.order.line'].create(vals)
|
||||
|
||||
def _get_purchase_schedule_date(self, values):
|
||||
"""Return the datetime value to use as Schedule Date (``date_planned``) for the
|
||||
Purchase Order Lines created to satisfy the given procurement. """
|
||||
procurement_date_planned = fields.Datetime.from_string(values['date_planned'])
|
||||
schedule_date = (procurement_date_planned - relativedelta(days=values['company_id'].po_lead))
|
||||
return schedule_date
|
||||
|
||||
def _get_purchase_order_date(self, product_id, product_qty, product_uom, values, partner, schedule_date):
|
||||
"""Return the datetime value to use as Order Date (``date_order``) for the
|
||||
Purchase Order created to satisfy the given procurement. """
|
||||
seller = product_id._select_seller(
|
||||
partner_id=partner,
|
||||
quantity=product_qty,
|
||||
date=fields.Date.to_string(schedule_date),
|
||||
uom_id=product_uom)
|
||||
|
||||
return schedule_date - relativedelta(days=int(seller.delay))
|
||||
|
||||
def _update_purchase_order_line(self, product_id, product_qty, product_uom, values, line, partner):
|
||||
procurement_uom_po_qty = product_uom._compute_quantity(product_qty, product_id.uom_po_id)
|
||||
seller = product_id._select_seller(
|
||||
partner_id=partner,
|
||||
quantity=line.product_qty + procurement_uom_po_qty,
|
||||
date=line.order_id.date_order and line.order_id.date_order[:10],
|
||||
uom_id=product_id.uom_po_id)
|
||||
|
||||
price_unit = self.env['account.tax']._fix_tax_included_price_company(seller.price, line.product_id.supplier_taxes_id, line.taxes_id, values['company_id']) if seller else 0.0
|
||||
if price_unit and seller and line.order_id.currency_id and seller.currency_id != line.order_id.currency_id:
|
||||
price_unit = seller.currency_id._convert(
|
||||
price_unit, line.order_id.currency_id, line.order_id.company_id, fields.Date.today())
|
||||
|
||||
return {
|
||||
'product_qty': line.product_qty + procurement_uom_po_qty,
|
||||
'price_unit': price_unit,
|
||||
'move_dest_ids': [(4, x.id) for x in values.get('move_dest_ids', [])]
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def _prepare_purchase_order_line(self, product_id, product_qty, product_uom, values, po, partner):
|
||||
procurement_uom_po_qty = product_uom._compute_quantity(product_qty, product_id.uom_po_id)
|
||||
seller = product_id._select_seller(
|
||||
partner_id=partner,
|
||||
quantity=procurement_uom_po_qty,
|
||||
date=po.date_order and po.date_order[:10],
|
||||
uom_id=product_id.uom_po_id)
|
||||
|
||||
taxes = product_id.supplier_taxes_id
|
||||
fpos = po.fiscal_position_id
|
||||
taxes_id = fpos.map_tax(taxes, product_id, seller.name) if fpos else taxes
|
||||
if taxes_id:
|
||||
taxes_id = taxes_id.filtered(lambda x: x.company_id.id == values['company_id'].id)
|
||||
|
||||
price_unit = self.env['account.tax']._fix_tax_included_price_company(seller.price, product_id.supplier_taxes_id, taxes_id, values['company_id']) if seller else 0.0
|
||||
if price_unit and seller and po.currency_id and seller.currency_id != po.currency_id:
|
||||
price_unit = seller.currency_id._convert(
|
||||
price_unit, po.currency_id, po.company_id, po.date_order or fields.Date.today())
|
||||
|
||||
product_lang = product_id.with_context({
|
||||
'lang': partner.lang,
|
||||
'partner_id': partner.id,
|
||||
})
|
||||
name = product_lang.display_name
|
||||
if product_lang.description_purchase:
|
||||
name += '\n' + product_lang.description_purchase
|
||||
|
||||
date_planned = self.env['purchase.order.line']._get_date_planned(seller, po=po).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
return {
|
||||
'name': name,
|
||||
'product_qty': procurement_uom_po_qty,
|
||||
'product_id': product_id.id,
|
||||
'product_uom': product_id.uom_po_id.id,
|
||||
'price_unit': price_unit,
|
||||
'date_planned': date_planned,
|
||||
'orderpoint_id': values.get('orderpoint_id', False) and values.get('orderpoint_id').id,
|
||||
'taxes_id': [(6, 0, taxes_id.ids)],
|
||||
'order_id': po.id,
|
||||
'move_dest_ids': [(4, x.id) for x in values.get('move_dest_ids', [])],
|
||||
}
|
||||
|
||||
def _prepare_purchase_order(self, product_id, product_qty, product_uom, origin, values, partner):
|
||||
schedule_date = self._get_purchase_schedule_date(values)
|
||||
purchase_date = self._get_purchase_order_date(product_id, product_qty, product_uom, values, partner, schedule_date)
|
||||
fpos = self.env['account.fiscal.position'].with_context(force_company=values['company_id'].id).get_fiscal_position(partner.id)
|
||||
|
||||
gpo = self.group_propagation_option
|
||||
group = (gpo == 'fixed' and self.group_id.id) or \
|
||||
(gpo == 'propagate' and 'group_id' in values and values['group_id'].id) or False
|
||||
|
||||
return {
|
||||
'partner_id': partner.id,
|
||||
'picking_type_id': self.picking_type_id.id,
|
||||
'company_id': values['company_id'].id,
|
||||
'currency_id': partner.property_purchase_currency_id.id or self.env.user.company_id.currency_id.id,
|
||||
'dest_address_id': values.get('partner_dest_id', False) and values['partner_dest_id'].id,
|
||||
'origin': origin,
|
||||
'payment_term_id': partner.property_supplier_payment_term_id.id,
|
||||
'date_order': purchase_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'fiscal_position_id': fpos,
|
||||
'group_id': group
|
||||
}
|
||||
|
||||
def _make_po_select_supplier(self, values, suppliers):
|
||||
""" Method intended to be overridden by customized modules to implement any logic in the
|
||||
selection of supplier.
|
||||
"""
|
||||
return suppliers[0]
|
||||
|
||||
def _make_po_get_domain(self, values, partner):
|
||||
domain = super(ProcurementRule, self)._make_po_get_domain(values, partner)
|
||||
gpo = self.group_propagation_option
|
||||
group = (gpo == 'fixed' and self.group_id) or \
|
||||
(gpo == 'propagate' and 'group_id' in values and values['group_id']) or False
|
||||
|
||||
domain += (
|
||||
('partner_id', '=', partner.id),
|
||||
('state', '=', 'draft'),
|
||||
('picking_type_id', '=', self.picking_type_id.id),
|
||||
('company_id', '=', values['company_id'].id),
|
||||
)
|
||||
if group:
|
||||
domain += (('group_id', '=', group.id),)
|
||||
return domain
|
||||
@@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.addons.base.models.res_partner import WARNING_MESSAGE, WARNING_HELP
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_name = 'product.template'
|
||||
_inherit = 'product.template'
|
||||
|
||||
@api.model
|
||||
def _get_buy_route(self):
|
||||
buy_route = self.env.ref('purchase.route_warehouse0_buy', raise_if_not_found=False)
|
||||
if buy_route:
|
||||
return buy_route.ids
|
||||
return []
|
||||
|
||||
@api.multi
|
||||
def _purchase_count(self):
|
||||
for template in self:
|
||||
template.purchase_count = sum([p.purchase_count for p in template.product_variant_ids])
|
||||
return True
|
||||
|
||||
property_account_creditor_price_difference = fields.Many2one(
|
||||
'account.account', string="Price Difference Account", company_dependent=True,
|
||||
help="This account will be used to value price difference between purchase price and cost price.")
|
||||
purchase_count = fields.Integer(compute='_purchase_count', string='# Purchases')
|
||||
purchase_method = fields.Selection([
|
||||
('purchase', 'On ordered quantities'),
|
||||
('receive', 'On received quantities'),
|
||||
], string="Control Policy", help="On ordered quantities: control bills based on ordered quantities.\n"
|
||||
"On received quantities: control bills based on received quantity.", default="receive")
|
||||
route_ids = fields.Many2many(default=lambda self: self._get_buy_route())
|
||||
purchase_line_warn = fields.Selection(WARNING_MESSAGE, 'Purchase Order Line', help=WARNING_HELP, required=True, default="no-message")
|
||||
purchase_line_warn_msg = fields.Text('Message for Purchase Order Line')
|
||||
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
_name = 'product.product'
|
||||
_inherit = 'product.product'
|
||||
|
||||
@api.multi
|
||||
def _purchase_count(self):
|
||||
domain = [
|
||||
('state', 'in', ['purchase', 'done']),
|
||||
('product_id', 'in', self.mapped('id')),
|
||||
]
|
||||
PurchaseOrderLines = self.env['purchase.order.line'].search(domain)
|
||||
for product in self:
|
||||
product.purchase_count = len(PurchaseOrderLines.filtered(lambda r: r.product_id == product).mapped('order_id'))
|
||||
|
||||
purchase_count = fields.Integer(compute='_purchase_count', string='# Purchases')
|
||||
|
||||
|
||||
class ProductCategory(models.Model):
|
||||
_inherit = "product.category"
|
||||
|
||||
property_account_creditor_price_difference_categ = fields.Many2one(
|
||||
'account.account', string="Price Difference Account",
|
||||
company_dependent=True,
|
||||
help="This account will be used to value price difference between purchase price and accounting cost.")
|
||||
@@ -809,7 +809,7 @@ class PurchaseOrderLine(models.Model):
|
||||
|
||||
:param Model seller: used to fetch the delivery delay (if no seller
|
||||
is provided, the delay is 0)
|
||||
:param Model po: purchase.order, necessary only if the PO line is
|
||||
:param Model po: purchase.order, necessary only if the PO line is
|
||||
not yet attached to a PO.
|
||||
:rtype: datetime
|
||||
:return: desired Schedule Date for the PO line
|
||||
@@ -921,236 +921,3 @@ class PurchaseOrderLine(models.Model):
|
||||
self.product_uom = seller_min_qty[0].product_uom
|
||||
else:
|
||||
self.product_qty = 1.0
|
||||
|
||||
|
||||
class ProcurementRule(models.Model):
|
||||
_inherit = 'procurement.rule'
|
||||
action = fields.Selection(selection_add=[('buy', 'Buy')])
|
||||
|
||||
@api.multi
|
||||
def _run_buy(self, product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
cache = {}
|
||||
suppliers = product_id.seller_ids\
|
||||
.filtered(lambda r: (not r.company_id or r.company_id == values['company_id']) and (not r.product_id or r.product_id == product_id))
|
||||
if not suppliers:
|
||||
msg = _('There is no vendor associated to the product %s. Please define a vendor for this product.') % (product_id.display_name,)
|
||||
raise UserError(msg)
|
||||
|
||||
supplier = self._make_po_select_supplier(values, suppliers)
|
||||
partner = supplier.name
|
||||
# we put `supplier_info` in values for extensibility purposes
|
||||
values['supplier'] = supplier
|
||||
|
||||
domain = self._make_po_get_domain(values, partner)
|
||||
if domain in cache:
|
||||
po = cache[domain]
|
||||
else:
|
||||
po = self.env['purchase.order'].search([dom for dom in domain])
|
||||
po = po[0] if po else False
|
||||
cache[domain] = po
|
||||
if not po:
|
||||
vals = self._prepare_purchase_order(product_id, product_qty, product_uom, origin, values, partner)
|
||||
po = self.env['purchase.order'].create(vals)
|
||||
cache[domain] = po
|
||||
elif not po.origin or origin not in po.origin.split(', '):
|
||||
if po.origin:
|
||||
if origin:
|
||||
po.write({'origin': po.origin + ', ' + origin})
|
||||
else:
|
||||
po.write({'origin': po.origin})
|
||||
else:
|
||||
po.write({'origin': origin})
|
||||
|
||||
# Create Line
|
||||
po_line = False
|
||||
for line in po.order_line:
|
||||
if line.product_id == product_id and line.product_uom == product_id.uom_po_id:
|
||||
if line._merge_in_existing_line(product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
vals = self._update_purchase_order_line(product_id, product_qty, product_uom, values, line, partner)
|
||||
po_line = line.write(vals)
|
||||
break
|
||||
if not po_line:
|
||||
vals = self._prepare_purchase_order_line(product_id, product_qty, product_uom, values, po, partner)
|
||||
self.env['purchase.order.line'].create(vals)
|
||||
|
||||
def _get_purchase_schedule_date(self, values):
|
||||
"""Return the datetime value to use as Schedule Date (``date_planned``) for the
|
||||
Purchase Order Lines created to satisfy the given procurement. """
|
||||
procurement_date_planned = fields.Datetime.from_string(values['date_planned'])
|
||||
schedule_date = (procurement_date_planned - relativedelta(days=values['company_id'].po_lead))
|
||||
return schedule_date
|
||||
|
||||
def _get_purchase_order_date(self, product_id, product_qty, product_uom, values, partner, schedule_date):
|
||||
"""Return the datetime value to use as Order Date (``date_order``) for the
|
||||
Purchase Order created to satisfy the given procurement. """
|
||||
seller = product_id._select_seller(
|
||||
partner_id=partner,
|
||||
quantity=product_qty,
|
||||
date=fields.Date.to_string(schedule_date),
|
||||
uom_id=product_uom)
|
||||
|
||||
return schedule_date - relativedelta(days=int(seller.delay))
|
||||
|
||||
def _update_purchase_order_line(self, product_id, product_qty, product_uom, values, line, partner):
|
||||
procurement_uom_po_qty = product_uom._compute_quantity(product_qty, product_id.uom_po_id)
|
||||
seller = product_id._select_seller(
|
||||
partner_id=partner,
|
||||
quantity=line.product_qty + procurement_uom_po_qty,
|
||||
date=line.order_id.date_order and line.order_id.date_order[:10],
|
||||
uom_id=product_id.uom_po_id)
|
||||
|
||||
price_unit = self.env['account.tax']._fix_tax_included_price_company(seller.price, line.product_id.supplier_taxes_id, line.taxes_id, values['company_id']) if seller else 0.0
|
||||
if price_unit and seller and line.order_id.currency_id and seller.currency_id != line.order_id.currency_id:
|
||||
price_unit = seller.currency_id._convert(
|
||||
price_unit, line.order_id.currency_id, line.order_id.company_id, fields.Date.today())
|
||||
|
||||
return {
|
||||
'product_qty': line.product_qty + procurement_uom_po_qty,
|
||||
'price_unit': price_unit,
|
||||
'move_dest_ids': [(4, x.id) for x in values.get('move_dest_ids', [])]
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def _prepare_purchase_order_line(self, product_id, product_qty, product_uom, values, po, partner):
|
||||
procurement_uom_po_qty = product_uom._compute_quantity(product_qty, product_id.uom_po_id)
|
||||
seller = product_id._select_seller(
|
||||
partner_id=partner,
|
||||
quantity=procurement_uom_po_qty,
|
||||
date=po.date_order and po.date_order[:10],
|
||||
uom_id=product_id.uom_po_id)
|
||||
|
||||
taxes = product_id.supplier_taxes_id
|
||||
fpos = po.fiscal_position_id
|
||||
taxes_id = fpos.map_tax(taxes, product_id, seller.name) if fpos else taxes
|
||||
if taxes_id:
|
||||
taxes_id = taxes_id.filtered(lambda x: x.company_id.id == values['company_id'].id)
|
||||
|
||||
price_unit = self.env['account.tax']._fix_tax_included_price_company(seller.price, product_id.supplier_taxes_id, taxes_id, values['company_id']) if seller else 0.0
|
||||
if price_unit and seller and po.currency_id and seller.currency_id != po.currency_id:
|
||||
price_unit = seller.currency_id._convert(
|
||||
price_unit, po.currency_id, po.company_id, po.date_order or fields.Date.today())
|
||||
|
||||
product_lang = product_id.with_context({
|
||||
'lang': partner.lang,
|
||||
'partner_id': partner.id,
|
||||
})
|
||||
name = product_lang.display_name
|
||||
if product_lang.description_purchase:
|
||||
name += '\n' + product_lang.description_purchase
|
||||
|
||||
date_planned = self.env['purchase.order.line']._get_date_planned(seller, po=po).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
return {
|
||||
'name': name,
|
||||
'product_qty': procurement_uom_po_qty,
|
||||
'product_id': product_id.id,
|
||||
'product_uom': product_id.uom_po_id.id,
|
||||
'price_unit': price_unit,
|
||||
'date_planned': date_planned,
|
||||
'orderpoint_id': values.get('orderpoint_id', False) and values.get('orderpoint_id').id,
|
||||
'taxes_id': [(6, 0, taxes_id.ids)],
|
||||
'order_id': po.id,
|
||||
'move_dest_ids': [(4, x.id) for x in values.get('move_dest_ids', [])],
|
||||
}
|
||||
|
||||
def _prepare_purchase_order(self, product_id, product_qty, product_uom, origin, values, partner):
|
||||
schedule_date = self._get_purchase_schedule_date(values)
|
||||
purchase_date = self._get_purchase_order_date(product_id, product_qty, product_uom, values, partner, schedule_date)
|
||||
fpos = self.env['account.fiscal.position'].with_context(force_company=values['company_id'].id).get_fiscal_position(partner.id)
|
||||
|
||||
gpo = self.group_propagation_option
|
||||
group = (gpo == 'fixed' and self.group_id.id) or \
|
||||
(gpo == 'propagate' and 'group_id' in values and values['group_id'].id) or False
|
||||
|
||||
return {
|
||||
'partner_id': partner.id,
|
||||
'picking_type_id': self.picking_type_id.id,
|
||||
'company_id': values['company_id'].id,
|
||||
'currency_id': partner.property_purchase_currency_id.id or self.env.user.company_id.currency_id.id,
|
||||
'dest_address_id': values.get('partner_dest_id', False) and values['partner_dest_id'].id,
|
||||
'origin': origin,
|
||||
'payment_term_id': partner.property_supplier_payment_term_id.id,
|
||||
'date_order': purchase_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'fiscal_position_id': fpos,
|
||||
'group_id': group
|
||||
}
|
||||
|
||||
def _make_po_select_supplier(self, values, suppliers):
|
||||
""" Method intended to be overridden by customized modules to implement any logic in the
|
||||
selection of supplier.
|
||||
"""
|
||||
return suppliers[0]
|
||||
|
||||
def _make_po_get_domain(self, values, partner):
|
||||
domain = super(ProcurementRule, self)._make_po_get_domain(values, partner)
|
||||
gpo = self.group_propagation_option
|
||||
group = (gpo == 'fixed' and self.group_id) or \
|
||||
(gpo == 'propagate' and 'group_id' in values and values['group_id']) or False
|
||||
|
||||
domain += (
|
||||
('partner_id', '=', partner.id),
|
||||
('state', '=', 'draft'),
|
||||
('picking_type_id', '=', self.picking_type_id.id),
|
||||
('company_id', '=', values['company_id'].id),
|
||||
)
|
||||
if group:
|
||||
domain += (('group_id', '=', group.id),)
|
||||
return domain
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_name = 'product.template'
|
||||
_inherit = 'product.template'
|
||||
|
||||
@api.model
|
||||
def _get_buy_route(self):
|
||||
buy_route = self.env.ref('purchase.route_warehouse0_buy', raise_if_not_found=False)
|
||||
if buy_route:
|
||||
return buy_route.ids
|
||||
return []
|
||||
|
||||
@api.multi
|
||||
def _purchase_count(self):
|
||||
for template in self:
|
||||
template.purchase_count = sum([p.purchase_count for p in template.product_variant_ids])
|
||||
return True
|
||||
|
||||
property_account_creditor_price_difference = fields.Many2one(
|
||||
'account.account', string="Price Difference Account", company_dependent=True,
|
||||
help="This account will be used to value price difference between purchase price and cost price.")
|
||||
purchase_count = fields.Integer(compute='_purchase_count', string='# Purchases')
|
||||
purchase_method = fields.Selection([
|
||||
('purchase', 'On ordered quantities'),
|
||||
('receive', 'On received quantities'),
|
||||
], string="Control Policy",
|
||||
help="On ordered quantities: control bills based on ordered quantities.\n"
|
||||
"On received quantities: control bills based on received quantity.", default="receive")
|
||||
route_ids = fields.Many2many(default=lambda self: self._get_buy_route())
|
||||
purchase_line_warn = fields.Selection(WARNING_MESSAGE, 'Purchase Order Line', help=WARNING_HELP, required=True, default="no-message")
|
||||
purchase_line_warn_msg = fields.Text('Message for Purchase Order Line')
|
||||
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
_name = 'product.product'
|
||||
_inherit = 'product.product'
|
||||
|
||||
@api.multi
|
||||
def _purchase_count(self):
|
||||
domain = [
|
||||
('state', 'in', ['purchase', 'done']),
|
||||
('product_id', 'in', self.mapped('id')),
|
||||
]
|
||||
PurchaseOrderLines = self.env['purchase.order.line'].search(domain)
|
||||
for product in self:
|
||||
product.purchase_count = len(PurchaseOrderLines.filtered(lambda r: r.product_id == product).mapped('order_id'))
|
||||
|
||||
purchase_count = fields.Integer(compute='_purchase_count', string='# Purchases')
|
||||
|
||||
|
||||
class ProductCategory(models.Model):
|
||||
_inherit = "product.category"
|
||||
|
||||
property_account_creditor_price_difference_categ = fields.Many2one(
|
||||
'account.account', string="Price Difference Account",
|
||||
company_dependent=True,
|
||||
help="This account will be used to value price difference between purchase price and accounting cost.")
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import purchase_report
|
||||
from . import purchase_bill
|
||||
|
||||
@@ -53,21 +53,3 @@ class PurchaseBillUnion(models.Model):
|
||||
name += ': ' + formatLang(self.env, amount, monetary=True, currency_obj=doc.currency_id)
|
||||
result.append((doc.id, name))
|
||||
return result
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
vendor_bill_purchase_id = fields.Many2one(
|
||||
comodel_name='purchase.bill.union',
|
||||
string='Auto-Complete'
|
||||
)
|
||||
|
||||
@api.onchange('vendor_bill_purchase_id')
|
||||
def _onchange_bill_purchase_order(self):
|
||||
if not self.vendor_bill_purchase_id:
|
||||
return {}
|
||||
self.purchase_id = self.vendor_bill_purchase_id.purchase_order_id
|
||||
self.vendor_bill_id = self.vendor_bill_purchase_id.vendor_bill_id
|
||||
self.vendor_bill_purchase_id = False
|
||||
return {}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_purchase_bill_union_filter" model="ir.ui.view">
|
||||
<field name="name">purchase.bill.union.select</field>
|
||||
<field name="model">purchase.bill.union</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Reference Document">
|
||||
<field name="name" string="Reference" filter_domain="['|', ('name','ilike',self), ('reference','=like',str(self)+'%')]"/>
|
||||
<field name="amount"/>
|
||||
<separator/>
|
||||
<field name="partner_id" operator="child_of"/>
|
||||
<separator/>
|
||||
<filter name="purchase_orders" string="Purchase Orders" domain="[('purchase_order_id', '!=', False)]"/>
|
||||
<filter name="vendor_bills" string="Vendor Bills" domain="[('vendor_bill_id', '!=', False)]"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_purchase_bill_union_tree" model="ir.ui.view">
|
||||
<field name="name">purchase.bill.union.tree</field>
|
||||
<field name="model">purchase.bill.union</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Reference Document">
|
||||
<field name="name"/>
|
||||
<field name="reference"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="date"/>
|
||||
<field name="amount"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -87,36 +87,4 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_purchase_bill_union_filter" model="ir.ui.view">
|
||||
<field name="name">purchase.bill.union.select</field>
|
||||
<field name="model">purchase.bill.union</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Reference Document">
|
||||
<field name="name" string="Reference" filter_domain="['|', ('name','ilike',self), ('reference','=like',str(self)+'%')]"/>
|
||||
<field name="amount"/>
|
||||
<separator/>
|
||||
<field name="partner_id" operator="child_of"/>
|
||||
<separator/>
|
||||
<filter name="purchase_orders" string="Purchase Orders" domain="[('purchase_order_id', '!=', False)]"/>
|
||||
<filter name="vendor_bills" string="Vendor Bills" domain="[('vendor_bill_id', '!=', False)]"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_purchase_bill_union_tree" model="ir.ui.view">
|
||||
<field name="name">purchase.bill.union.tree</field>
|
||||
<field name="model">purchase.bill.union</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Reference Document">
|
||||
<field name="name"/>
|
||||
<field name="reference"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="date"/>
|
||||
<field name="amount"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Product Suppliers-->
|
||||
|
||||
<record id="view_product_supplier_inherit" model="ir.ui.view">
|
||||
<field name="name">product.template.supplier.form.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<group name="purchase" position="before">
|
||||
<group string="Vendors" name="vendors" attrs="{'invisible': [('product_variant_count','>',1)]}">
|
||||
<field name="seller_ids" context="{'default_product_tmpl_id':context.get('product_tmpl_id',active_id), 'product_template_invisible_variant': True}" nolabel="1"/>
|
||||
</group>
|
||||
<group string="Vendors" name="variant_vendors" attrs="{'invisible': [('product_variant_count','<=',1)]}">
|
||||
<field name="variant_seller_ids" context="{'default_product_tmpl_id': context.get('product_tmpl_id', active_id)}" nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group name="bill" position="attributes">
|
||||
<attribute name="groups">purchase.group_purchase_manager</attribute>
|
||||
</group>
|
||||
<group name="bill" position="inside">
|
||||
<field name="purchase_method" widget="radio"/>
|
||||
</group>
|
||||
<page name="purchase" position="inside">
|
||||
<group string="Description for Vendors">
|
||||
<field name="description_purchase" nolabel="1"
|
||||
placeholder="This note will show up on purchase orders."/>
|
||||
</group>
|
||||
<group string="Warning when Purchasing this Product" groups="purchase.group_warning_purchase">
|
||||
<field name="purchase_line_warn" nolabel="1"/>
|
||||
<field name="purchase_line_warn_msg" colspan="3" nolabel="1"
|
||||
attrs="{'required':[('purchase_line_warn','!=','no-message')],'readonly':[('purchase_line_warn','=','no-message')], 'invisible':[('purchase_line_warn','=','no-message')]}"/>
|
||||
</group>
|
||||
</page>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_category_property_form" model="ir.ui.view">
|
||||
<field name="name">product.category.property.form.inherit.stock</field>
|
||||
<field name="model">product.category</field>
|
||||
<field name="inherit_id" ref="account.view_category_property_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="property_account_income_categ_id" position="before">
|
||||
<field name="property_account_creditor_price_difference_categ" domain="[('deprecated','=',False)]" groups="account.group_account_user"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_purchase_line_product_tree">
|
||||
<field name="context">{}</field>
|
||||
<field name="domain">[('product_id.product_tmpl_id','in',active_ids), ('state', 'in', ['purchase', 'done'])]</field>
|
||||
<field name="name">Purchase Order Lines</field>
|
||||
<field name="res_model">purchase.order.line</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="purchase_order_line_tree"/>
|
||||
</record>
|
||||
|
||||
<record id="view_product_account_purchase_ok_form" model="ir.ui.view">
|
||||
<field name="name">product.template.account.purchase.ok.form.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="account.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="property_account_expense_id" position="attributes">
|
||||
<attribute name="domain">[('deprecated','=',False)]</attribute>
|
||||
<attribute name="attrs">{'readonly': [('purchase_ok', '=', 0)]}</attribute>
|
||||
</field>
|
||||
<field name='supplier_taxes_id' position="replace" >
|
||||
<field name="supplier_taxes_id" colspan="2" widget="many2many_tags" attrs="{'readonly':[('purchase_ok','=',0)]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_product_template_purchase_buttons_from" model="ir.ui.view">
|
||||
<field name="name">product.template.purchase.button.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_only_form_view"/>
|
||||
<field name="groups_id" eval="[(4, ref('purchase.group_purchase_user'))]"/>
|
||||
<field name="arch" type="xml">
|
||||
<button name="toggle_active" position="before">
|
||||
<button class="oe_stat_button" name="%(purchase.action_purchase_line_product_tree)d"
|
||||
type="action" icon="fa-shopping-cart" attrs="{'invisible': [('purchase_ok', '=', False)]}">
|
||||
<field string="Purchases" name="purchase_count" widget="statinfo"/>
|
||||
</button>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_template_form_view" model="ir.ui.view">
|
||||
<field name="name">product.normal.form.inherit.stock</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="account.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="property_account_expense_id" position="after">
|
||||
<field name="property_account_creditor_price_difference" domain="[('deprecated','=',False)]" attrs="{'readonly':[('purchase_ok', '=', 0)]}" groups="account.group_account_user"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -128,14 +128,6 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="purchase_open_picking" model="ir.actions.act_window">
|
||||
<field name="name">Receptions</field>
|
||||
<field name="res_model">stock.picking</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('purchase_id', '=', active_id)]</field>
|
||||
</record>
|
||||
|
||||
<record id="purchase_order_form" model="ir.ui.view">
|
||||
<field name="name">purchase.order.form</field>
|
||||
<field name="model">purchase.order</field>
|
||||
@@ -171,7 +163,7 @@
|
||||
icon="fa-pencil-square-o" attrs="{'invisible':[('state', 'in', ('draft','sent','to approve')),('invoice_ids','=',[])]}">
|
||||
<field name="invoice_count" widget="statinfo" string="Vendor Bills"/>
|
||||
<field name='invoice_ids' invisible="1"/>
|
||||
</button>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label string="Request for Quotation " attrs="{'invisible': [('state','not in',('draft','sent','bid'))]}"/>
|
||||
@@ -518,99 +510,4 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Product Suppliers-->
|
||||
|
||||
<record id="view_product_supplier_inherit" model="ir.ui.view">
|
||||
<field name="name">product.template.supplier.form.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<group name="purchase" position="before">
|
||||
<group string="Vendors" name="vendors" attrs="{'invisible': [('product_variant_count','>',1)]}">
|
||||
<field name="seller_ids" context="{'default_product_tmpl_id':context.get('product_tmpl_id',active_id), 'product_template_invisible_variant': True}" nolabel="1"/>
|
||||
</group>
|
||||
<group string="Vendors" name="variant_vendors" attrs="{'invisible': [('product_variant_count','<=',1)]}">
|
||||
<field name="variant_seller_ids" context="{'default_product_tmpl_id': context.get('product_tmpl_id', active_id)}" nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group name="bill" position="attributes">
|
||||
<attribute name="groups">purchase.group_purchase_manager</attribute>
|
||||
</group>
|
||||
<group name="bill" position="inside">
|
||||
<field name="purchase_method" widget="radio"/>
|
||||
</group>
|
||||
<page name="purchase" position="inside">
|
||||
<group string="Description for Vendors">
|
||||
<field name="description_purchase" nolabel="1"
|
||||
placeholder="This note will show up on purchase orders."/>
|
||||
</group>
|
||||
<group string="Warning when Purchasing this Product" groups="purchase.group_warning_purchase">
|
||||
<field name="purchase_line_warn" nolabel="1"/>
|
||||
<field name="purchase_line_warn_msg" colspan="3" nolabel="1"
|
||||
attrs="{'required':[('purchase_line_warn','!=','no-message')],'readonly':[('purchase_line_warn','=','no-message')], 'invisible':[('purchase_line_warn','=','no-message')]}"/>
|
||||
</group>
|
||||
</page>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_category_property_form" model="ir.ui.view">
|
||||
<field name="name">product.category.property.form.inherit.stock</field>
|
||||
<field name="model">product.category</field>
|
||||
<field name="inherit_id" ref="account.view_category_property_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="property_account_income_categ_id" position="before">
|
||||
<field name="property_account_creditor_price_difference_categ" domain="[('deprecated','=',False)]" groups="account.group_account_user"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_purchase_line_product_tree">
|
||||
<field name="context">{}</field>
|
||||
<field name="domain">[('product_id.product_tmpl_id','in',active_ids), ('state', 'in', ['purchase', 'done'])]</field>
|
||||
<field name="name">Purchase Order Lines</field>
|
||||
<field name="res_model">purchase.order.line</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="purchase_order_line_tree"/>
|
||||
</record>
|
||||
|
||||
<record id="view_product_account_purchase_ok_form" model="ir.ui.view">
|
||||
<field name="name">product.template.account.purchase.ok.form.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="account.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="property_account_expense_id" position="attributes">
|
||||
<attribute name="domain">[('deprecated','=',False)]</attribute>
|
||||
<attribute name="attrs">{'readonly': [('purchase_ok', '=', 0)]}</attribute>
|
||||
</field>
|
||||
<field name='supplier_taxes_id' position="replace" >
|
||||
<field name="supplier_taxes_id" colspan="2" widget="many2many_tags" attrs="{'readonly':[('purchase_ok','=',0)]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_product_template_purchase_buttons_from" model="ir.ui.view">
|
||||
<field name="name">product.template.purchase.button.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_only_form_view"/>
|
||||
<field name="groups_id" eval="[(4, ref('purchase.group_purchase_user'))]"/>
|
||||
<field name="arch" type="xml">
|
||||
<button name="toggle_active" position="before">
|
||||
<button class="oe_stat_button" name="%(purchase.action_purchase_line_product_tree)d"
|
||||
type="action" icon="fa-shopping-cart" attrs="{'invisible': [('purchase_ok', '=', False)]}">
|
||||
<field string="Purchases" name="purchase_count" widget="statinfo"/>
|
||||
</button>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_template_form_view" model="ir.ui.view">
|
||||
<field name="name">product.normal.form.inherit.stock</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="account.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="property_account_expense_id" position="after">
|
||||
<field name="property_account_creditor_price_difference" domain="[('deprecated','=',False)]" attrs="{'readonly':[('purchase_ok', '=', 0)]}" groups="account.group_account_user"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- Purchase button on orderpoints -->
|
||||
|
||||
<record id="view_warehouse_orderpoint_purchase_form" model="ir.ui.view">
|
||||
@@ -40,4 +40,11 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="purchase_open_picking" model="ir.actions.act_window">
|
||||
<field name="name">Receptions</field>
|
||||
<field name="res_model">stock.picking</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('purchase_id', '=', active_id)]</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user