[IMP] purchase, purchase_requisition: improve UX
Several improvements in order to improve user interface are done here. [IMP] purchase: Prevent create PO&RFQ in calendar It was possible to create PO's and RFQ's from the calendar view. The Product Owner wanted to prevent the users from doing so. [IMP] purchase: Add tooltip to Receipt Date [FIX] purchase: Align report & stat button data on product form The use of Last 365 days time_range in the purchase analysis was automatically excluding today's purchases wich was confusing users as the stat button info was taking those into account. Different behaviours were implemented for product template and product product in the filters that were used which makes no sense. Use of context_today() instead of datetime.now() in order to take the user's timezone into account. [IMP] purchase: Use relevant dates for RFQ's and PO's in calendar view The date used in order to display RFQs and POs in the calendar was date_planned which is not mandatory and so not always filled. It has been decided to use the order_date for the RFQ's and date_approve for PO's. [IMP] purchase_requisition: Making Agreement selection type more explicit [IMP] purchase: Show UOM menu only if installed [IMP] purchase: Add product variant in settings [IMP] purchase: Remove Favorites predefined filters [IMP] purchase: Rephrase PO action helper [IMP] purchase: Allow open/edit Agreement Type [IMP] purchase: Rephrase RFQ action helper [IMP] purchase: Remove PO's and RFQ's name from fields in calendar The Product Owner originally wanted to have a link on the PO name field that was shown in the calendat popover. As the Edit button already allows to edit the PO's or RFQ's from the popover it was decided to simply remove the field (as it was already displayed at the top of the popover) [IMP] purchase: Open the right view when click on Reporting top menu Task ID #2196688 Closes #47810 Related: odoo/enterprise#9294 Related: odoo/upgrade#961 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
committed by
Thibault Delavallée
parent
30ab8ac022
commit
38294dbc99
@@ -5,6 +5,7 @@ from datetime import timedelta
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.addons.base.models.res_partner import WARNING_MESSAGE, WARNING_HELP
|
||||
from odoo.tools.float_utils import float_round
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
@@ -43,8 +44,7 @@ class ProductTemplate(models.Model):
|
||||
action['domain'] = ['&', ('state', 'in', ['purchase', 'done']), ('product_tmpl_id', 'in', self.ids)]
|
||||
action['context'] = {
|
||||
'graph_measure': 'qty_ordered',
|
||||
'search_default_orders': 1,
|
||||
'time_ranges': {'field': 'date_approve', 'range': 'last_365_days'}
|
||||
'search_default_later_than_a_year_ago': True
|
||||
}
|
||||
return action
|
||||
|
||||
@@ -56,13 +56,12 @@ class ProductProduct(models.Model):
|
||||
purchased_product_qty = fields.Float(compute='_compute_purchased_product_qty', string='Purchased')
|
||||
|
||||
def _compute_purchased_product_qty(self):
|
||||
date_from = fields.Datetime.to_string(fields.datetime.now() - timedelta(days=365))
|
||||
date_from = fields.Datetime.to_string(fields.Date.context_today(self) - relativedelta(years=1))
|
||||
domain = [
|
||||
('state', 'in', ['purchase', 'done']),
|
||||
('order_id.state', 'in', ['purchase', 'done']),
|
||||
('product_id', 'in', self.ids),
|
||||
('date_order', '>', date_from)
|
||||
('order_id.date_approve', '>=', date_from)
|
||||
]
|
||||
PurchaseOrderLines = self.env['purchase.order.line'].search(domain)
|
||||
order_lines = self.env['purchase.order.line'].read_group(domain, ['product_id', 'product_uom_qty'], ['product_id'])
|
||||
purchased_data = dict([(data['product_id'][0], data['product_uom_qty']) for data in order_lines])
|
||||
for product in self:
|
||||
@@ -75,9 +74,8 @@ class ProductProduct(models.Model):
|
||||
action = self.env.ref('purchase.action_purchase_order_report_all').read()[0]
|
||||
action['domain'] = ['&', ('state', 'in', ['purchase', 'done']), ('product_id', 'in', self.ids)]
|
||||
action['context'] = {
|
||||
'search_default_last_year_purchase': 1,
|
||||
'search_default_status': 1, 'search_default_order_month': 1,
|
||||
'graph_measure': 'qty_ordered'
|
||||
'graph_measure': 'qty_ordered',
|
||||
'search_default_later_than_a_year_ago': True
|
||||
}
|
||||
return action
|
||||
|
||||
|
||||
@@ -98,7 +98,9 @@ class PurchaseOrder(models.Model):
|
||||
], string='Billing Status', compute='_get_invoiced', store=True, readonly=True, copy=False, default='no')
|
||||
|
||||
# There is no inverse function on purpose since the date may be different on each line
|
||||
date_planned = fields.Datetime(string='Receipt Date', index=True)
|
||||
date_planned = fields.Datetime(string='Receipt Date', index=True,
|
||||
help='This is the Receipt Date promised by the supplier. If set, the receipt will be scheduled at this date.')
|
||||
date_calendar_start = fields.Datetime(compute='_compute_date_calendar_start', readonly=True, store=True)
|
||||
|
||||
amount_untaxed = fields.Monetary(string='Untaxed Amount', store=True, readonly=True, compute='_amount_all', tracking=True)
|
||||
amount_tax = fields.Monetary(string='Taxes', store=True, readonly=True, compute='_amount_all')
|
||||
@@ -128,6 +130,11 @@ class PurchaseOrder(models.Model):
|
||||
for order in self:
|
||||
order.access_url = '/my/purchase/%s' % (order.id)
|
||||
|
||||
@api.depends('state', 'date_order', 'date_approve')
|
||||
def _compute_date_calendar_start(self):
|
||||
for order in self:
|
||||
order.date_calendar_start = order.date_approve if (order.state in ['purchase', 'done']) else order.date_order
|
||||
|
||||
@api.model
|
||||
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
|
||||
args = args or []
|
||||
|
||||
@@ -23,29 +23,6 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Custom reports (aka filters) -->
|
||||
<record id="filter_purchase_order_monthly_purchases" model="ir.filters">
|
||||
<field name="name">Monthly Purchases</field>
|
||||
<field name="model_id">purchase.report</field>
|
||||
<field name="domain">[('state','!=','cancel')]</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
</record>
|
||||
<record id="filter_purchase_order_price_per_supplier" model="ir.filters">
|
||||
<field name="name">Price Per Vendor</field>
|
||||
<field name="model_id">purchase.report</field>
|
||||
<field name="domain">[('state','!=','draft'),('state','!=','cancel')]</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
<field name="context">{'group_by': ['partner_id'], 'col_group_by': ['product_id'], 'measures': ['price_average']}</field>
|
||||
</record>
|
||||
<record id="filter_purchase_order_average_delivery_time" model="ir.filters">
|
||||
<field name="name">Average Delivery Time</field>
|
||||
<field name="model_id">purchase.report</field>
|
||||
<field name="domain">[('state','!=','draft'),('state','!=','cancel')]</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
<field name="context">{'group_by': ['partner_id'], 'measures': ['delay_pass']}</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="view_purchase_order_search" model="ir.ui.view">
|
||||
<field name="name">report.purchase.order.search</field>
|
||||
<field name="model">purchase.report</field>
|
||||
@@ -53,6 +30,7 @@
|
||||
<search string="Purchase Orders">
|
||||
<filter string="Requests for Quotation" name="quotes" domain="[('state','in',('draft','sent'))]"/>
|
||||
<filter string="Purchase Orders" name="orders" domain="[('state','!=','draft'), ('state','!=','sent'), ('state','!=','cancel')]"/>
|
||||
<filter string="Confirmation Date Last Year" name="later_than_a_year_ago" domain="[('date_approve', '>=', ((context_today()-relativedelta(years=1)).strftime('%Y-%m-%d')))]"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="product_id"/>
|
||||
<group expand="0" string="Extended Filters">
|
||||
@@ -87,8 +65,6 @@
|
||||
<field name="target">current</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="purchase_report" name="Reporting" parent="purchase.menu_purchase_root" sequence="99"/>
|
||||
|
||||
<menuitem id="menu_report_purchase" name="Purchase" action="action_purchase_order_report_all" parent="purchase_report" sequence="1" groups="purchase.group_purchase_manager"/>
|
||||
<menuitem id="purchase_report" name="Reporting" parent="purchase.menu_purchase_root" sequence="99" groups="purchase.group_purchase_manager" action="action_purchase_order_report_all"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -24,19 +24,31 @@
|
||||
|
||||
<menuitem
|
||||
id="menu_product_in_config_purchase" name="Products"
|
||||
parent="menu_purchase_config" sequence="30" groups="base.group_no_one"/>
|
||||
parent="menu_purchase_config" sequence="30"/>
|
||||
|
||||
<record model="ir.ui.menu" id="menu_product_in_config_purchase">
|
||||
<field name="groups_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_product_attribute_action"
|
||||
action="product.attribute_action" name="Product Attributes"
|
||||
parent="purchase.menu_product_in_config_purchase" groups="product.group_product_variant" sequence="1"/>
|
||||
|
||||
<menuitem
|
||||
action="product.product_category_action_form" id="menu_product_category_config_purchase"
|
||||
parent="purchase.menu_product_in_config_purchase" sequence="1" />
|
||||
parent="purchase.menu_product_in_config_purchase" sequence="3" />
|
||||
|
||||
<menuitem
|
||||
action="uom.product_uom_categ_form_action" id="menu_purchase_uom_categ_form_action"
|
||||
parent="purchase.menu_product_in_config_purchase" sequence="10" groups="uom.group_uom"/>
|
||||
<menuitem
|
||||
id="menu_unit_of_measure_in_config_purchase" name="Units of Measures"
|
||||
parent="menu_purchase_config" sequence="40" groups="uom.group_uom" />
|
||||
|
||||
<menuitem
|
||||
action="uom.product_uom_form_action" id="menu_purchase_uom_form_action"
|
||||
parent="purchase.menu_product_in_config_purchase" sequence="5" groups="uom.group_uom"/>
|
||||
parent="purchase.menu_unit_of_measure_in_config_purchase" sequence="5" groups="uom.group_uom"/>
|
||||
|
||||
<menuitem
|
||||
action="uom.product_uom_categ_form_action" id="menu_purchase_uom_categ_form_action"
|
||||
parent="purchase.menu_unit_of_measure_in_config_purchase" sequence="10" groups="uom.group_uom"/>
|
||||
|
||||
<!-- Products Control Menu -->
|
||||
<menuitem id="menu_purchase_products" name="Products" parent="purchase.menu_purchase_root" sequence="5"/>
|
||||
@@ -87,9 +99,8 @@
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="priority" eval="2"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar string="Calendar View" date_start="date_planned" color="partner_id" hide_time="true" event_limit="5">
|
||||
<calendar string="Calendar View" date_start="date_calendar_start" color="partner_id" hide_time="true" event_limit="5" create="false">
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field name="partner_ref"/>
|
||||
<field name="amount_total" widget="monetary"/>
|
||||
<field name="partner_id" filters="1"/>
|
||||
@@ -548,8 +559,8 @@
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a request for quotation
|
||||
</p><p>
|
||||
The quotation contains the history of the discussion
|
||||
you had with your vendor.
|
||||
The requests for quotation are documents that will be sent to your suppliers to request prices for different products you consider buying.
|
||||
Once an agreement has been found with the supplier, they will be confirmed and turned into purchase orders.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
@@ -568,9 +579,9 @@
|
||||
<field name="context">{}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a quotation
|
||||
Create a purchase order
|
||||
</p><p>
|
||||
It will be converted into a purchase order.
|
||||
Once you ordered your products to your supplier, confirm your request for quotation and it will turn into a purchase order.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -109,6 +109,23 @@
|
||||
</div>
|
||||
<h2>Products</h2>
|
||||
<div class="row mt16 o_settings_container" name="matrix_setting_container">
|
||||
<div class="col-12 col-lg-6 o_setting_box" id="variant_options">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="group_product_variant"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="group_product_variant"/>
|
||||
<a href="https://www.odoo.com/documentation/user/13.0/sales/products_prices/products/variants.html" title="Documentation" class="o_doc_link fa fa-lg fa-book" target="_blank"></a>
|
||||
<div class="text-muted">
|
||||
Purchase variants of a product using attributes (size, color, etc.)
|
||||
</div>
|
||||
<div class="content-group" attrs="{'invisible': [('group_product_variant','=',False)]}">
|
||||
<div class="mt8">
|
||||
<button name="%(product.attribute_action)d" icon="fa-arrow-right" type="action" string="Attributes" class="btn-link"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 o_setting_box"
|
||||
id="product_matrix"
|
||||
title="If installed, the product variants will be added to purchase orders through a grid entry.">
|
||||
|
||||
@@ -24,10 +24,10 @@ class PurchaseRequisitionType(models.Model):
|
||||
name = fields.Char(string='Agreement Type', required=True, translate=True)
|
||||
sequence = fields.Integer(default=1)
|
||||
exclusive = fields.Selection([
|
||||
('exclusive', 'Select only one RFQ (exclusive)'), ('multiple', 'Select multiple RFQ')],
|
||||
('exclusive', 'Select only one RFQ (exclusive)'), ('multiple', 'Select multiple RFQ (non-exclusive)')],
|
||||
string='Agreement Selection Type', required=True, default='multiple',
|
||||
help="""Select only one RFQ (exclusive): when a purchase order is confirmed, cancel the remaining purchase order.\n
|
||||
Select multiple RFQ: allows multiple purchase orders. On confirmation of a purchase order it does not cancel the remaining orders""")
|
||||
Select multiple RFQ (non-exclusive): allows multiple purchase orders. On confirmation of a purchase order it does not cancel the remaining orders""")
|
||||
quantity_copy = fields.Selection([
|
||||
('copy', 'Use quantities of agreement'), ('none', 'Set quantities manually')],
|
||||
string='Quantities', required=True, default='none')
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
<group>
|
||||
<field name="is_quantity_copy" invisible='1'/>
|
||||
<field name="user_id" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}" domain="[('share', '=', False)]"/>
|
||||
<field name="type_id" attrs="{'readonly': [('state','!=','draft')]}" widget="selection"/>
|
||||
<field name="type_id" attrs="{'readonly': [('state','!=','draft')]}"/>
|
||||
<field name="vendor_id" context="{'res_partner_search_mode': 'supplier'}" attrs="{'required': [('is_quantity_copy', '=', 'none')], 'readonly': [('state', 'in', ['ongoing','done'])]}"/>
|
||||
<field name="currency_id" groups="base.group_multi_currency"/>
|
||||
</group>
|
||||
|
||||
Reference in New Issue
Block a user