[IMP] purchase: purchase report improvement

Modify purchase report in order to make it consistent with
sales report.
For the RFQ it will show the Order Date and for the Purchase order
menu it will display the Confirmation Date.

Also rewrite a bit the SQL view in order to compute every purchase
order in the company currency.

Technicaly rewrite the SQL alias in order to have a report more
readable in the future.

Task ID : 1857130.

closes odoo/odoo#28248

Signed-off-by: Arnold Moyaux <amoyaux@users.noreply.github.com>
This commit is contained in:
Hetashree Chauhan
2019-05-31 09:06:30 +00:00
committed by Arnold Moyaux
parent 2d4b026625
commit b5cea7e2b6
8 changed files with 182 additions and 85 deletions
+16 -16
View File
@@ -19,21 +19,21 @@
'price_unit': 79.80, 'price_unit': 79.80,
'product_qty': 15.0, 'product_qty': 15.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today() + relativedelta(days=3)}),
(0, 0, { (0, 0, {
'product_id': ref('product.product_product_25'), 'product_id': ref('product.product_product_25'),
'name': obj().env.ref('product.product_product_25').partner_ref, 'name': obj().env.ref('product.product_product_25').partner_ref,
'price_unit': 2868.70, 'price_unit': 2868.70,
'product_qty': 5.0, 'product_qty': 5.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today() + relativedelta(days=3)}),
(0, 0, { (0, 0, {
'product_id': ref('product.product_product_27'), 'product_id': ref('product.product_product_27'),
'name': obj().env.ref('product.product_product_27').partner_ref, 'name': obj().env.ref('product.product_product_27').partner_ref,
'price_unit': 3297.20, 'price_unit': 3297.20,
'product_qty': 4.0, 'product_qty': 4.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}) 'date_planned': DateTime.today() + relativedelta(days=3)})
]"/> ]"/>
</record> </record>
@@ -48,14 +48,14 @@
'price_unit': 132.50, 'price_unit': 132.50,
'product_qty': 20.0, 'product_qty': 20.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today() + relativedelta(days=1)}),
(0, 0, { (0, 0, {
'product_id': ref('product.product_delivery_01'), 'product_id': ref('product.product_delivery_01'),
'name': obj().env.ref('product.product_delivery_01').partner_ref, 'name': obj().env.ref('product.product_delivery_01').partner_ref,
'price_unit': 89.0, 'price_unit': 89.0,
'product_qty': 5.0, 'product_qty': 5.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today() + relativedelta(days=1)}),
]"/> ]"/>
</record> </record>
@@ -70,7 +70,7 @@
'price_unit': 25.50, 'price_unit': 25.50,
'product_qty': 10.0, 'product_qty': 10.0,
'product_uom': ref('uom.product_uom_hour'), 'product_uom': ref('uom.product_uom_hour'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today() + relativedelta(days=1)}),
]"/> ]"/>
</record> </record>
@@ -85,21 +85,21 @@
'price_unit': 85.50, 'price_unit': 85.50,
'product_qty': 6.0, 'product_qty': 6.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today() + relativedelta(days=5)}),
(0, 0, { (0, 0, {
'product_id': ref('product.product_product_20'), 'product_id': ref('product.product_product_20'),
'name': obj().env.ref('product.product_product_20').partner_ref, 'name': obj().env.ref('product.product_product_20').partner_ref,
'price_unit': 1690.0, 'price_unit': 1690.0,
'product_qty': 5.0, 'product_qty': 5.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today() + relativedelta(days=5)}),
(0, 0, { (0, 0, {
'product_id': ref('product.product_product_6'), 'product_id': ref('product.product_product_6'),
'name': obj().env.ref('product.product_product_6').partner_ref, 'name': obj().env.ref('product.product_product_6').partner_ref,
'price_unit': 800.0, 'price_unit': 800.0,
'product_qty': 7.0, 'product_qty': 7.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}) 'date_planned': DateTime.today() + relativedelta(days=5)})
]"/> ]"/>
</record> </record>
@@ -114,14 +114,14 @@
'price_unit': 2010.0, 'price_unit': 2010.0,
'product_qty': 3.0, 'product_qty': 3.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today()}),
(0, 0, { (0, 0, {
'product_id': ref('product.product_product_24'), 'product_id': ref('product.product_product_24'),
'name': obj().env.ref('product.product_product_24').partner_ref, 'name': obj().env.ref('product.product_product_24').partner_ref,
'price_unit': 876.0, 'price_unit': 876.0,
'product_qty': 3.0, 'product_qty': 3.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today()}),
]"/> ]"/>
</record> </record>
@@ -136,21 +136,21 @@
'price_unit': 58.0, 'price_unit': 58.0,
'product_qty': 9.0, 'product_qty': 9.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today()}),
(0, 0, { (0, 0, {
'product_id': ref('product.product_delivery_01'), 'product_id': ref('product.product_delivery_01'),
'name': obj().env.ref('product.product_delivery_01').partner_ref, 'name': obj().env.ref('product.product_delivery_01').partner_ref,
'price_unit': 65.0, 'price_unit': 65.0,
'product_qty': 3.0, 'product_qty': 3.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today()}),
(0, 0, { (0, 0, {
'product_id': ref('product.consu_delivery_01'), 'product_id': ref('product.consu_delivery_01'),
'name': obj().env.ref('product.consu_delivery_01').partner_ref, 'name': obj().env.ref('product.consu_delivery_01').partner_ref,
'price_unit': 154.5, 'price_unit': 154.5,
'product_qty': 4.0, 'product_qty': 4.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today()}),
]"/> ]"/>
</record> </record>
@@ -165,14 +165,14 @@
'price_unit': 13.5, 'price_unit': 13.5,
'product_qty': 5.0, 'product_qty': 5.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today()}),
(0, 0, { (0, 0, {
'product_id': ref('product.product_delivery_02'), 'product_id': ref('product.product_delivery_02'),
'name': obj().env.ref('product.product_delivery_02').partner_ref, 'name': obj().env.ref('product.product_delivery_02').partner_ref,
'price_unit': 38.0, 'price_unit': 38.0,
'product_qty': 15.0, 'product_qty': 15.0,
'product_uom': ref('uom.product_uom_unit'), 'product_uom': ref('uom.product_uom_unit'),
'date_planned': time.strftime('%Y-%m-%d')}), 'date_planned': DateTime.today()}),
]"/> ]"/>
</record> </record>
+7 -1
View File
@@ -85,7 +85,7 @@ class PurchaseOrder(models.Model):
"delivery order sent by your vendor.") "delivery order sent by your vendor.")
date_order = fields.Datetime('Order Date', required=True, states=READONLY_STATES, index=True, copy=False, default=fields.Datetime.now,\ date_order = fields.Datetime('Order Date', required=True, states=READONLY_STATES, index=True, copy=False, default=fields.Datetime.now,\
help="Depicts the date where the Quotation should be validated and converted into a purchase order.") help="Depicts the date where the Quotation should be validated and converted into a purchase order.")
date_approve = fields.Date('Confirmation Date', readonly=1, index=True, copy=False) date_approve = fields.Datetime('Confirmation Date', readonly=1, index=True, copy=False)
partner_id = fields.Many2one('res.partner', string='Vendor', required=True, states=READONLY_STATES, change_default=True, tracking=True, help="You can find a vendor by its Name, TIN, Email or Internal Reference.") partner_id = fields.Many2one('res.partner', string='Vendor', required=True, states=READONLY_STATES, change_default=True, tracking=True, help="You can find a vendor by its Name, TIN, Email or Internal Reference.")
dest_address_id = fields.Many2one('res.partner', string='Drop Ship Address', states=READONLY_STATES, dest_address_id = fields.Many2one('res.partner', string='Drop Ship Address', states=READONLY_STATES,
help="Put an address if you want to deliver directly from the vendor to the customer. " help="Put an address if you want to deliver directly from the vendor to the customer. "
@@ -125,6 +125,7 @@ class PurchaseOrder(models.Model):
product_id = fields.Many2one('product.product', related='order_line.product_id', string='Product', readonly=False) product_id = fields.Many2one('product.product', related='order_line.product_id', string='Product', readonly=False)
user_id = fields.Many2one('res.users', string='Purchase Representative', index=True, tracking=True, default=lambda self: self.env.user) user_id = fields.Many2one('res.users', string='Purchase Representative', index=True, tracking=True, default=lambda self: self.env.user)
company_id = fields.Many2one('res.company', 'Company', required=True, index=True, states=READONLY_STATES, default=lambda self: self.env.company.id) company_id = fields.Many2one('res.company', 'Company', required=True, index=True, states=READONLY_STATES, default=lambda self: self.env.company.id)
currency_rate = fields.Float("Currency Rate", compute='_compute_currency_rate', compute_sudo=True, store=True, readonly=True, help='Ratio between the purchase order currency and the company currency')
def _compute_access_url(self): def _compute_access_url(self):
super(PurchaseOrder, self)._compute_access_url() super(PurchaseOrder, self)._compute_access_url()
@@ -140,6 +141,11 @@ class PurchaseOrder(models.Model):
purchase_order_ids = self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid) purchase_order_ids = self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid)
return self.browse(purchase_order_ids).name_get() return self.browse(purchase_order_ids).name_get()
@api.depends('date_order', 'currency_id', 'company_id', 'company_id.currency_id')
def _compute_currency_rate(self):
for order in self:
order.currency_rate = self.env['res.currency']._get_conversion_rate(order.company_id.currency_id, order.currency_id, order.company_id, order.date_order)
@api.multi @api.multi
@api.depends('name', 'partner_ref') @api.depends('name', 'partner_ref')
def name_get(self): def name_get(self):
+56 -50
View File
@@ -25,18 +25,15 @@ class PurchaseReport(models.Model):
], 'Order Status', readonly=True) ], 'Order Status', readonly=True)
product_id = fields.Many2one('product.product', 'Product', readonly=True) product_id = fields.Many2one('product.product', 'Product', readonly=True)
partner_id = fields.Many2one('res.partner', 'Vendor', readonly=True) partner_id = fields.Many2one('res.partner', 'Vendor', readonly=True)
date_approve = fields.Date('Date Approved', readonly=True) date_approve = fields.Datetime('Confirmation Date', readonly=True)
product_uom = fields.Many2one('uom.uom', 'Reference Unit of Measure', required=True) product_uom = fields.Many2one('uom.uom', 'Reference Unit of Measure', required=True)
company_id = fields.Many2one('res.company', 'Company', readonly=True) company_id = fields.Many2one('res.company', 'Company', readonly=True)
currency_id = fields.Many2one('res.currency', 'Currency', readonly=True) currency_id = fields.Many2one('res.currency', 'Currency', readonly=True)
user_id = fields.Many2one('res.users', 'Purchase Representative', readonly=True) user_id = fields.Many2one('res.users', 'Purchase Representative', readonly=True)
delay = fields.Float('Days to Validate', digits=(16, 2), readonly=True) delay = fields.Float('Days to Confirm', digits=(16, 2), readonly=True)
delay_pass = fields.Float('Days to Deliver', digits=(16, 2), readonly=True) delay_pass = fields.Float('Days to Receive', digits=(16, 2), readonly=True)
unit_quantity = fields.Float('Product Quantity', readonly=True, oldname='quantity') price_total = fields.Float('Total', readonly=True)
price_total = fields.Float('Total Price', readonly=True) price_average = fields.Float('Average Cost', readonly=True, group_operator="avg")
price_average = fields.Float('Average Price', readonly=True, group_operator="avg")
negociation = fields.Float('Purchase-Standard Price', readonly=True, group_operator="avg")
price_standard = fields.Float('Products Value', readonly=True, group_operator="sum")
nbr_lines = fields.Integer('# of Lines', readonly=True, oldname='nbr') nbr_lines = fields.Integer('# of Lines', readonly=True, oldname='nbr')
category_id = fields.Many2one('product.category', 'Product Category', readonly=True) category_id = fields.Many2one('product.category', 'Product Category', readonly=True)
product_tmpl_id = fields.Many2one('product.template', 'Product Template', readonly=True) product_tmpl_id = fields.Many2one('product.template', 'Product Template', readonly=True)
@@ -46,6 +43,12 @@ class PurchaseReport(models.Model):
commercial_partner_id = fields.Many2one('res.partner', 'Commercial Entity', readonly=True) commercial_partner_id = fields.Many2one('res.partner', 'Commercial Entity', readonly=True)
weight = fields.Float('Gross Weight', readonly=True) weight = fields.Float('Gross Weight', readonly=True)
volume = fields.Float('Volume', readonly=True) volume = fields.Float('Volume', readonly=True)
order_id = fields.Many2one('purchase.order', 'Order', readonly=True)
untaxed_total = fields.Float('Untaxed Total', readonly=True)
qty_ordered = fields.Float('Qty Ordered', readonly=True)
qty_received = fields.Float('Qty Received', readonly=True)
qty_billed = fields.Float('Qty Billed', readonly=True)
qty_to_be_billed = fields.Float('Qty to be Billed', readonly=True)
@api.model_cr @api.model_cr
def init(self): def init(self):
@@ -61,80 +64,83 @@ class PurchaseReport(models.Model):
select_str = """ select_str = """
WITH currency_rate as (%s) WITH currency_rate as (%s)
SELECT SELECT
po.id as order_id,
min(l.id) as id, min(l.id) as id,
s.date_order as date_order, po.date_order as date_order,
s.state, po.state,
s.date_approve, po.date_approve,
s.dest_address_id, po.dest_address_id,
s.partner_id as partner_id, po.partner_id as partner_id,
s.user_id as user_id, po.user_id as user_id,
s.company_id as company_id, po.company_id as company_id,
s.fiscal_position_id as fiscal_position_id, po.fiscal_position_id as fiscal_position_id,
l.product_id, l.product_id,
p.product_tmpl_id, p.product_tmpl_id,
t.categ_id as category_id, t.categ_id as category_id,
s.currency_id, po.currency_id,
t.uom_id as product_uom, t.uom_id as product_uom,
sum(l.product_qty/u.factor*u2.factor) as unit_quantity, extract(epoch from age(po.date_approve,po.date_order))/(24*60*60)::decimal(16,2) as delay,
extract(epoch from age(s.date_approve,s.date_order))/(24*60*60)::decimal(16,2) as delay, extract(epoch from age(l.date_planned,po.date_order))/(24*60*60)::decimal(16,2) as delay_pass,
extract(epoch from age(l.date_planned,s.date_order))/(24*60*60)::decimal(16,2) as delay_pass,
count(*) as nbr_lines, count(*) as nbr_lines,
sum(l.price_unit / COALESCE(NULLIF(cr.rate, 0), 1.0) * l.product_qty)::decimal(16,2) as price_total, sum(l.price_total / COALESCE(po.currency_rate, 1.0))::decimal(16,2) as price_total,
avg(100.0 * (l.price_unit / COALESCE(NULLIF(cr.rate, 0),1.0) * l.product_qty) / NULLIF(ip.value_float*l.product_qty/u.factor*u2.factor, 0.0))::decimal(16,2) as negociation, (sum(l.product_qty * l.price_unit / COALESCE(po.currency_rate, 1.0))/NULLIF(sum(l.product_qty/line_uom.factor*product_uom.factor),0.0))::decimal(16,2) as price_average,
sum(ip.value_float*l.product_qty/u.factor*u2.factor)::decimal(16,2) as price_standard,
(sum(l.product_qty * l.price_unit / COALESCE(NULLIF(cr.rate, 0), 1.0))/NULLIF(sum(l.product_qty/u.factor*u2.factor),0.0))::decimal(16,2) as price_average,
partner.country_id as country_id, partner.country_id as country_id,
partner.commercial_partner_id as commercial_partner_id, partner.commercial_partner_id as commercial_partner_id,
analytic_account.id as account_analytic_id, analytic_account.id as account_analytic_id,
sum(p.weight * l.product_qty/u.factor*u2.factor) as weight, sum(p.weight * l.product_qty/line_uom.factor*product_uom.factor) as weight,
sum(p.volume * l.product_qty/u.factor*u2.factor) as volume sum(p.volume * l.product_qty/line_uom.factor*product_uom.factor) as volume,
sum(l.price_subtotal / COALESCE(po.currency_rate, 1.0))::decimal(16,2) as untaxed_total,
sum(l.product_qty / line_uom.factor * product_uom.factor) as qty_ordered,
sum(l.qty_received / line_uom.factor * product_uom.factor) as qty_received,
sum(l.qty_invoiced / line_uom.factor * product_uom.factor) as qty_billed,
sum(l.product_qty / line_uom.factor * product_uom.factor) - sum(l.qty_received / line_uom.factor * product_uom.factor) as qty_to_be_billed
""" % self.env['res.currency']._select_companies_rates() """ % self.env['res.currency']._select_companies_rates()
return select_str return select_str
def _from(self): def _from(self):
from_str = """ from_str = """
purchase_order_line l purchase_order_line l
join purchase_order s on (l.order_id=s.id) join purchase_order po on (l.order_id=po.id)
join res_partner partner on s.partner_id = partner.id join res_partner partner on po.partner_id = partner.id
left join product_product p on (l.product_id=p.id) left join product_product p on (l.product_id=p.id)
left join product_template t on (p.product_tmpl_id=t.id) left join product_template t on (p.product_tmpl_id=t.id)
LEFT JOIN ir_property ip ON (ip.name='standard_price' AND ip.res_id=CONCAT('product.product,',p.id) AND ip.company_id=s.company_id) left join uom_uom line_uom on (line_uom.id=l.product_uom)
left join uom_uom u on (u.id=l.product_uom) left join uom_uom product_uom on (product_uom.id=t.uom_id)
left join uom_uom u2 on (u2.id=t.uom_id)
left join account_analytic_account analytic_account on (l.account_analytic_id = analytic_account.id) left join account_analytic_account analytic_account on (l.account_analytic_id = analytic_account.id)
left join currency_rate cr on (cr.currency_id = s.currency_id and left join currency_rate cr on (cr.currency_id = po.currency_id and
cr.company_id = s.company_id and cr.company_id = po.company_id and
cr.date_start <= coalesce(s.date_order, now()) and cr.date_start <= coalesce(po.date_order, now()) and
(cr.date_end is null or cr.date_end > coalesce(s.date_order, now()))) (cr.date_end is null or cr.date_end > coalesce(po.date_order, now())))
""" """
return from_str return from_str
def _group_by(self): def _group_by(self):
group_by_str = """ group_by_str = """
GROUP BY GROUP BY
s.company_id, po.company_id,
s.user_id, po.user_id,
s.partner_id, po.partner_id,
u.factor, line_uom.factor,
s.currency_id, po.currency_id,
l.price_unit, l.price_unit,
s.date_approve, po.date_approve,
l.date_planned, l.date_planned,
l.product_uom, l.product_uom,
s.dest_address_id, po.dest_address_id,
s.fiscal_position_id, po.fiscal_position_id,
l.product_id, l.product_id,
p.product_tmpl_id, p.product_tmpl_id,
t.categ_id, t.categ_id,
s.date_order, po.date_order,
s.state, po.state,
u.uom_type, line_uom.uom_type,
u.category_id, line_uom.category_id,
t.uom_id, t.uom_id,
u.id, line_uom.id,
u2.factor, product_uom.factor,
partner.country_id, partner.country_id,
partner.commercial_partner_id, partner.commercial_partner_id,
analytic_account.id analytic_account.id,
po.id
""" """
return group_by_str return group_by_str
@@ -4,12 +4,11 @@
<field name="name">product.month.pivot</field> <field name="name">product.month.pivot</field>
<field name="model">purchase.report</field> <field name="model">purchase.report</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<pivot string="Purchase Orders Statistics" disable_linking="True"> <pivot string="Purchase Analysis" disable_linking="True" display_quantity="true">
<field name="partner_id" type="row"/> <field name="category_id" type="row"/>
<field name="date_order" interval="month" type="col"/> <field name="order_id" type="measure"/>
<field name="untaxed_total" type="measure"/>
<field name="price_total" type="measure"/> <field name="price_total" type="measure"/>
<field name="unit_quantity" type="measure"/>
<field name="price_average" type="measure"/>
</pivot> </pivot>
</field> </field>
</record> </record>
@@ -17,10 +16,9 @@
<field name="name">product.month.graph</field> <field name="name">product.month.graph</field>
<field name="model">purchase.report</field> <field name="model">purchase.report</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<graph string="Purchase Orders Statistics"> <graph string="Purchase Orders Statistics" type="line">
<field name="partner_id" type="row"/> <field name="date_approve" interval="day" type="col"/>
<field name="date_order" interval="month" type="col"/> <field name="untaxed_total" type="measure"/>
<field name="price_average" type="measure"/>
</graph> </graph>
</field> </field>
</record> </record>
@@ -31,7 +29,6 @@
<field name="model_id">purchase.report</field> <field name="model_id">purchase.report</field>
<field name="domain">[('state','!=','cancel')]</field> <field name="domain">[('state','!=','cancel')]</field>
<field name="user_id" eval="False"/> <field name="user_id" eval="False"/>
<field name="context">{'group_by': ['category_id'], 'col_group_by': ['date_order:month'], 'measures': ['unit_quantity']}</field>
</record> </record>
<record id="filter_purchase_order_price_per_supplier" model="ir.filters"> <record id="filter_purchase_order_price_per_supplier" model="ir.filters">
<field name="name">Price Per Vendor</field> <field name="name">Price Per Vendor</field>
@@ -54,8 +51,8 @@
<field name="model">purchase.report</field> <field name="model">purchase.report</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Purchase Orders"> <search string="Purchase Orders">
<filter string="Quotations" name="quotes" domain="[('state','=','draft')]"/> <filter string="Requests for Quotation" name="quotes" domain="[('state','=','draft')]"/>
<filter string="Orders" name="orders" domain="[('state','!=','draft'),('state','!=','cancel')]"/> <filter string="Purchase Orders" name="orders" domain="[('state','!=','draft'),('state','!=','cancel')]"/>
<field name="partner_id"/> <field name="partner_id"/>
<field name="product_id"/> <field name="product_id"/>
<group expand="0" string="Extended Filters"> <group expand="0" string="Extended Filters">
@@ -69,12 +66,13 @@
<filter string="Vendor" name="group_partner_id" context="{'group_by':'partner_id'}"/> <filter string="Vendor" name="group_partner_id" context="{'group_by':'partner_id'}"/>
<filter string="Vendor Country" name="country_id" context="{'group_by':'country_id'}"/> <filter string="Vendor Country" name="country_id" context="{'group_by':'country_id'}"/>
<filter string="Purchase Representative" name="user_id" context="{'group_by':'user_id'}"/> <filter string="Purchase Representative" name="user_id" context="{'group_by':'user_id'}"/>
<filter string="Product" name="group_product_id" context="{'group_by':'product_id'}"/>
<filter string="Product Category" name="group_category_id" context="{'group_by':'category_id'}"/> <filter string="Product Category" name="group_category_id" context="{'group_by':'category_id'}"/>
<filter string="Fiscal Position" name="fiscal_position" context="{'group_by':'fiscal_position_id'}" help="Purchase Order Fiscal Position"/>
<filter string="Status" name="status" context="{'group_by':'state'}"/> <filter string="Status" name="status" context="{'group_by':'state'}"/>
<filter string="Company" name="company" context="{'group_by':'company_id'}" groups="base.group_multi_company"/> <filter string="Company" name="company" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
<separator/> <separator/>
<filter string="Order Date" name="order_month" context="{'group_by': 'date_order:month'}"/> <filter string="Order Date" name="order_month" context="{'group_by': 'date_order:month'}"/>
<filter string="Confirmation Date" name="group_date_approve_month" context="{'group_by': 'date_approve:month'}"/>
</group> </group>
</search> </search>
</field> </field>
@@ -90,7 +88,8 @@
<field name="target">current</field> <field name="target">current</field>
</record> </record>
<menuitem id="purchase_report" name="Reporting" parent="purchase.menu_purchase_root" sequence="99" <menuitem id="purchase_report" name="Reporting" parent="purchase.menu_purchase_root" sequence="99"/>
groups="purchase.group_purchase_manager" action="action_purchase_order_report_all"/>
<menuitem id="menu_report_purchase" name="Purchase" action="action_purchase_order_report_all" parent="purchase_report" sequence="1" groups="purchase.group_purchase_manager"/>
</odoo> </odoo>
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_purchase_order_report
@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime
from odoo.tests import common, Form, tagged
@tagged('post_install', '-at_install')
class TestPurchaseOrderReport(common.TransactionCase):
def setUp(self):
super(TestPurchaseOrderReport, self).setUp()
self.partner_id = self.env.ref('base.res_partner_1')
self.product1 = self.env.ref('product.product_product_8')
self.product2 = self.env.ref('product.product_product_11')
self.PurchaseReport = self.env['purchase.report']
# Create a new company and set CoA
self.company_id = self.env['res.company'].create({'name': 'new_company'})
self.env.user.company_id = self.company_id
self.env.ref('l10n_generic_coa.configurable_chart_template').load_for_current_company(False, False)
def test_00_purchase_order_report(self):
uom_dozen = self.env.ref('uom.product_uom_dozen')
eur_currency = self.env.ref('base.EUR')
self.company_id.currency_id = self.env.ref('base.USD').id
self.env['res.currency.rate'].search([]).unlink()
self.env['res.currency.rate'].create({
'name': datetime.today(),
'rate': 2.0,
'currency_id': eur_currency.id,
})
po = self.env['purchase.order'].create({
'partner_id': self.partner_id.id,
'currency_id': eur_currency.id,
'order_line': [
(0, 0, {
'name': self.product1.name,
'product_id': self.product1.id,
'product_qty': 1.0,
'product_uom': uom_dozen.id,
'price_unit': 100.0,
'date_planned': datetime.today(),
}),
(0, 0, {
'name': self.product2.name,
'product_id': self.product2.id,
'product_qty': 1.0,
'product_uom': uom_dozen.id,
'price_unit': 200.0,
'date_planned': datetime.today(),
}),
],
})
po.button_confirm()
f = Form(self.env['account.invoice'])
f.partner_id = po.partner_id
f.purchase_id = po
invoice = f.save()
invoice.action_invoice_open()
res_product1 = self.PurchaseReport.search([
('order_id', '=', po.id), ('product_id', '=', self.product1.id)])
# check that report will convert dozen to unit or not
self.assertEquals(res_product1.qty_ordered, 12.0, 'UoM conversion is not working')
# report should show in company currency (amount/rate) = (100/2)
self.assertEquals(res_product1.price_total, 50.0, 'Currency conversion is not working')
res_product2 = self.PurchaseReport.search([
('order_id', '=', po.id), ('product_id', '=', self.product2.id)])
# Check that repost should show 6 unit of product
self.assertEquals(res_product2.qty_ordered, 12.0, 'UoM conversion is not working')
# report should show in company currency (amount/rate) = (200/2)
self.assertEquals(res_product2.price_total, 100.0, 'Currency conversion is not working')
+5 -2
View File
@@ -175,7 +175,8 @@
<field name="currency_id" groups="base.group_multi_currency"/> <field name="currency_id" groups="base.group_multi_currency"/>
</group> </group>
<group> <group>
<field name="date_order"/> <field name="date_order" attrs="{'invisible': [('state','=','purchase')]}"/>
<field name="date_approve" attrs="{'invisible': [('state','!=','purchase')]}"/>
<field name="origin" attrs="{'invisible': [('origin','=',False)]}"/> <field name="origin" attrs="{'invisible': [('origin','=',False)]}"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/> <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
</group> </group>
@@ -374,7 +375,8 @@
<tree decoration-bf="message_unread==True" decoration-muted="state=='cancel'" decoration-info="state in ('wait','confirmed')" string="Purchase Order"> <tree decoration-bf="message_unread==True" decoration-muted="state=='cancel'" decoration-info="state in ('wait','confirmed')" string="Purchase Order">
<field name="message_unread" invisible="1"/> <field name="message_unread" invisible="1"/>
<field name="name" string="Reference"/> <field name="name" string="Reference"/>
<field name="date_order" /> <field name="date_order" invisible="not context.get('quotation_only', False)"/>
<field name="date_approve" invisible="context.get('quotation_only', False)"/>
<field name="partner_id"/> <field name="partner_id"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/> <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
<field name="date_planned" invisible="context.get('quotation_only', False)"/> <field name="date_planned" invisible="context.get('quotation_only', False)"/>
@@ -420,6 +422,7 @@
<field name="res_model">purchase.order</field> <field name="res_model">purchase.order</field>
<field name="view_mode">tree,kanban,form,pivot,graph,calendar,activity</field> <field name="view_mode">tree,kanban,form,pivot,graph,calendar,activity</field>
<field name="search_view_id" ref="view_purchase_order_filter"/> <field name="search_view_id" ref="view_purchase_order_filter"/>
<field name="context">{'quotation_only': True}</field>
<field name="help" type="html"> <field name="help" type="html">
<p class="o_view_nocontent_smiling_face"> <p class="o_view_nocontent_smiling_face">
Create a request for quotation Create a request for quotation
@@ -13,7 +13,7 @@ class PurchaseReport(models.Model):
return super(PurchaseReport, self)._select() + ", spt.warehouse_id as picking_type_id" return super(PurchaseReport, self)._select() + ", spt.warehouse_id as picking_type_id"
def _from(self): def _from(self):
return super(PurchaseReport, self)._from() + " left join stock_picking_type spt on (spt.id=s.picking_type_id)" return super(PurchaseReport, self)._from() + " left join stock_picking_type spt on (spt.id=po.picking_type_id)"
def _group_by(self): def _group_by(self):
return super(PurchaseReport, self)._group_by() + ", spt.warehouse_id" return super(PurchaseReport, self)._group_by() + ", spt.warehouse_id"