[ADD]: Add dependency
ii) move wizard , yaml file iii) move stock method into sale_stock. iv) add security into sale_stock module. bzr revid: atp@tinyerp.com-20120726205659-xiyx39r3dibshyjs
This commit is contained in:
parent
5e93bcd949
commit
06c0ead10d
|
@ -38,7 +38,7 @@ when you confirm your sale order it will automatically create a registration for
|
|||
this event.
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['event','sale','sale_crm'],
|
||||
'depends': ['event', 'sale_crm'],
|
||||
'update_xml': [
|
||||
'event_sale_view.xml',
|
||||
],
|
||||
|
|
|
@ -34,7 +34,7 @@ This module is the base module for other multi-company modules.
|
|||
'website': 'http://www.openerp.com/',
|
||||
'depends': [
|
||||
'base',
|
||||
'sale',
|
||||
'sale_stock',
|
||||
'project',
|
||||
],
|
||||
'init_xml': [],
|
||||
|
|
|
@ -30,7 +30,7 @@ This module adds sale menu and features to your portal if sale and portal are
|
|||
installed.
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['sale','portal'],
|
||||
'depends': ['sale_stock','portal'],
|
||||
'data': [
|
||||
'security/portal_security.xml',
|
||||
'portal_sale_view.xml',
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
<field eval=""""Procurement Task"""" name="name"/>
|
||||
<field eval=""""if product type is 'service' then it creates the task."""" name="note"/>
|
||||
<field model="process.node" name="target_node_id" ref="process_node_procuretasktask0"/>
|
||||
<field model="process.node" name="source_node_id" ref="sale.process_node_saleprocurement0"/>
|
||||
<field model="process.node" name="source_node_id" ref="sale_stock.process_node_saleprocurement0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_createtask0" model="process.transition">
|
||||
|
|
|
@ -24,11 +24,9 @@
|
|||
#----------------------------------------------------------
|
||||
|
||||
import sale
|
||||
import stock
|
||||
import res_partner
|
||||
import wizard
|
||||
import report
|
||||
import company
|
||||
import edi
|
||||
import res_config
|
||||
|
||||
|
|
|
@ -35,8 +35,6 @@ Workflow with validation steps:
|
|||
|
||||
Create Invoice:
|
||||
---------------
|
||||
* Invoice on Demand
|
||||
* Invoice on Delivery Order
|
||||
* Invoice Before Delivery
|
||||
|
||||
Partners preferences:
|
||||
|
@ -45,16 +43,6 @@ Partners preferences:
|
|||
* Shipping
|
||||
* Invoicing
|
||||
|
||||
Products stocks and prices:
|
||||
--------------------------
|
||||
|
||||
Delivery method:
|
||||
-----------------
|
||||
* The Poste
|
||||
* Free Delivery Charges
|
||||
* Normal Delivery Charges
|
||||
* Based on the Delivery Order(if not Add to sale order)
|
||||
|
||||
Dashboard for Sales Manager that includes:
|
||||
------------------------------------------
|
||||
* My Quotations
|
||||
|
@ -63,15 +51,11 @@ Dashboard for Sales Manager that includes:
|
|||
'author': 'OpenERP SA',
|
||||
'website': 'http://www.openerp.com',
|
||||
'images': ['images/deliveries_to_invoice.jpeg','images/sale_dashboard.jpeg','images/Sale_order_line_to_invoice.jpeg','images/sale_order.jpeg','images/sales_analysis.jpeg'],
|
||||
'depends': ['stock', 'procurement', 'board', 'account_voucher'],
|
||||
'depends': ['board', 'account_voucher'],
|
||||
'init_xml': [],
|
||||
'update_xml': [
|
||||
'wizard/sale_make_invoice_advance.xml',
|
||||
'wizard/sale_line_invoice.xml',
|
||||
'wizard/sale_make_invoice.xml',
|
||||
'update_xml': ['wizard/sale_line_invoice.xml',
|
||||
'security/sale_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'company_view.xml',
|
||||
'sale_workflow.xml',
|
||||
'sale_sequence.xml',
|
||||
'sale_report.xml',
|
||||
|
@ -79,7 +63,6 @@ Dashboard for Sales Manager that includes:
|
|||
'sale_view.xml',
|
||||
'res_partner_view.xml',
|
||||
'report/sale_report_view.xml',
|
||||
'stock_view.xml',
|
||||
'process/sale_process.xml',
|
||||
'board_sale_view.xml',
|
||||
'edi/sale_order_action_data.xml',
|
||||
|
@ -88,8 +71,6 @@ Dashboard for Sales Manager that includes:
|
|||
'demo_xml': ['sale_demo.xml'],
|
||||
'test': [
|
||||
'test/sale_order_demo.yml',
|
||||
'test/picking_order_policy.yml',
|
||||
'test/manual_order_policy.yml',
|
||||
'test/prepaid_order_policy.yml',
|
||||
'test/cancel_order.yml',
|
||||
'test/delete_order.yml',
|
||||
|
|
|
@ -185,16 +185,17 @@ class sale_order(osv.osv, EDIMixin):
|
|||
|
||||
order_lines = edi_document['order_line']
|
||||
for order_line in order_lines:
|
||||
self._edi_requires_attributes(('date_planned', 'product_id', 'product_uom', 'product_qty', 'price_unit'), order_line)
|
||||
self._edi_requires_attributes(( 'product_id', 'product_uom', 'product_qty', 'price_unit'), order_line)
|
||||
order_line['product_uom_qty'] = order_line['product_qty']
|
||||
del order_line['product_qty']
|
||||
date_planned = order_line.pop('date_planned')
|
||||
delay = 0
|
||||
if date_order and date_planned:
|
||||
# no security_days buffer, this is the promised date given by supplier
|
||||
delay = (datetime.strptime(date_planned, DEFAULT_SERVER_DATE_FORMAT) - \
|
||||
datetime.strptime(date_order, DEFAULT_SERVER_DATE_FORMAT)).days
|
||||
order_line['delay'] = delay
|
||||
#Need to move sale_stock
|
||||
# date_planned = order_line.pop('date_planned')
|
||||
# delay = 0
|
||||
# if date_order and date_planned:
|
||||
# # no security_days buffer, this is the promised date given by supplier
|
||||
# delay = (datetime.strptime(date_planned, DEFAULT_SERVER_DATE_FORMAT) - \
|
||||
# datetime.strptime(date_order, DEFAULT_SERVER_DATE_FORMAT)).days
|
||||
# order_line['delay'] = delay
|
||||
|
||||
# discard web preview fields, if present
|
||||
order_line.pop('price_subtotal', None)
|
||||
|
@ -218,9 +219,9 @@ class sale_order_line(osv.osv, EDIMixin):
|
|||
|
||||
# company.security_days is for internal use, so customer should only
|
||||
# see the expected date_planned based on line.delay
|
||||
date_planned = datetime.strptime(line.order_id.date_order, DEFAULT_SERVER_DATE_FORMAT) + \
|
||||
relativedelta(days=line.delay or 0.0)
|
||||
edi_doc['date_planned'] = date_planned.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
# date_planned = datetime.strptime(line.order_id.date_order, DEFAULT_SERVER_DATE_FORMAT) + \
|
||||
# relativedelta(days=line.delay or 0.0)
|
||||
# edi_doc['date_planned'] = date_planned.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
edi_doc_list.append(edi_doc)
|
||||
return edi_doc_list
|
||||
|
||||
|
|
|
@ -28,10 +28,10 @@
|
|||
<!-- Mail template and workflow bindings are done in a NOUPDATE block
|
||||
so users can freely customize/delete them -->
|
||||
<data noupdate="1">
|
||||
<!-- bind the mailing server action to sale.order confirmed activity -->
|
||||
<!-- bind the mailing server action to sale.order confirmed activity
|
||||
<record id="sale.act_wait_ship" model="workflow.activity">
|
||||
<field name="action_id" ref="ir_actions_server_edi_sale"/>
|
||||
</record>
|
||||
</record> -->
|
||||
|
||||
|
||||
<!--Email template -->
|
||||
|
|
|
@ -28,18 +28,6 @@
|
|||
<field eval="1" name="flow_start"/>
|
||||
</record>
|
||||
|
||||
<record id="process_node_saleorderprocurement0" model="process.node">
|
||||
<field name="menu_id" ref="sale.menu_sale_order"/>
|
||||
<field name="model_id" ref="sale.model_sale_order"/>
|
||||
<field eval=""""subflow"""" name="kind"/>
|
||||
<field eval=""""Sales Order Requisition"""" name="name"/>
|
||||
<field eval=""""Drives procurement orders for every sales order line."""" name="note"/>
|
||||
<field name="subflow_id" ref="process_process_salesprocess0"/>
|
||||
<field name="process_id" ref="procurement.process_process_procurementprocess0"/>
|
||||
<field eval=""""object.state in ('draft', 'waiting_date', 'manual', 'progress', 'shipping_except', 'invoice_except', 'done', 'cancel')"""" name="model_states"/>
|
||||
<field eval="1" name="flow_start"/>
|
||||
</record>
|
||||
|
||||
<record id="process_node_quotation0" model="process.node">
|
||||
<field name="menu_id" ref="sale.menu_sale_order"/>
|
||||
<field name="model_id" ref="sale.model_sale_order"/>
|
||||
|
@ -62,38 +50,6 @@
|
|||
<field eval="0" name="flow_start"/>
|
||||
</record>
|
||||
|
||||
<record id="process_node_saleprocurement0" model="process.node">
|
||||
<field name="menu_id" ref="procurement.menu_stock_procurement_action"/>
|
||||
<field name="model_id" ref="procurement.model_procurement_order"/>
|
||||
<field eval=""""subflow"""" name="kind"/>
|
||||
<field eval=""""Procurement Order"""" name="name"/>
|
||||
<field eval=""""One Procurement order for each sales order line and for each of the components."""" name="note"/>
|
||||
<field name="subflow_id" ref="procurement.process_process_procurementprocess0"/>
|
||||
<field name="process_id" ref="process_process_salesprocess0"/>
|
||||
<field eval=""""object.state=='confirmed'"""" name="model_states"/>
|
||||
<field eval="1" name="flow_start"/>
|
||||
</record>
|
||||
|
||||
<record id="process_node_packinglist0" model="process.node">
|
||||
<field name="model_id" ref="stock.model_stock_picking"/>
|
||||
<field eval=""""state"""" name="kind"/>
|
||||
<field eval=""""Pick List"""" name="name"/>
|
||||
<field eval=""""Document of the move to the output or to the customer."""" name="note"/>
|
||||
<field name="process_id" ref="process_process_salesprocess0"/>
|
||||
<field eval=""""object.state in ('assigned', 'confirmed')"""" name="model_states"/>
|
||||
<field eval="0" name="flow_start"/>
|
||||
</record>
|
||||
|
||||
<record id="process_node_deliveryorder0" model="process.node">
|
||||
<field name="model_id" ref="stock.model_stock_picking"/>
|
||||
<field eval=""""state"""" name="kind"/>
|
||||
<field eval=""""Delivery Order"""" name="name"/>
|
||||
<field eval=""""Document of the move to the customer."""" name="note"/>
|
||||
<field name="process_id" ref="process_process_salesprocess0"/>
|
||||
<field eval=""""object.state in ('done', 'assigned')"""" name="model_states"/>
|
||||
<field eval="0" name="flow_start"/>
|
||||
</record>
|
||||
|
||||
<record id="process_node_invoice0" model="process.node">
|
||||
<field name="menu_id" ref="account.menu_action_invoice_tree1"/>
|
||||
<field name="model_id" ref="account.model_account_invoice"/>
|
||||
|
@ -142,54 +98,6 @@
|
|||
<field eval="[(6,0,[ref('sale.trans_draft_router')])]" name="transition_ids"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_saleprocurement0" model="process.transition">
|
||||
<field eval="[(6,0,[])]" name="transition_ids"/>
|
||||
<field eval=""""Create Procurement Order"""" name="name"/>
|
||||
<field eval=""""A procurement order is automatically created as soon as a sales order is confirmed or as the invoice is paid. It drives the purchasing and the production of products regarding to the rules and to the sales order's parameters. """" name="note"/>
|
||||
<field model="process.node" name="target_node_id" ref="process_node_saleprocurement0"/>
|
||||
<field model="process.node" name="source_node_id" ref="process_node_saleorder0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_packing0" model="process.transition">
|
||||
<field eval="[(6,0,[])]" name="transition_ids"/>
|
||||
<field eval=""""Create Pick List"""" name="name"/>
|
||||
<field eval=""""The Pick List form is created as soon as the sales order is confirmed, in the same time as the procurement order. It represents the assignment of parts to the sales order. There is 1 pick list by sales order line which evolves with the availability of parts."""" name="note"/>
|
||||
<field model="process.node" name="target_node_id" ref="process_node_packinglist0"/>
|
||||
<field model="process.node" name="source_node_id" ref="process_node_saleprocurement0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_deliver0" model="process.transition">
|
||||
<field eval="[(6,0,[])]" name="transition_ids"/>
|
||||
<field eval=""""Create Delivery Order"""" name="name"/>
|
||||
<field eval=""""Depending on the configuration of the location Output, the move between the output area and the customer is done through the Delivery Order manually or automatically."""" name="note"/>
|
||||
<field model="process.node" name="target_node_id" ref="process_node_deliveryorder0"/>
|
||||
<field model="process.node" name="source_node_id" ref="process_node_packinglist0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_invoice0" model="process.transition">
|
||||
<field eval="[(6,0,[])]" name="transition_ids"/>
|
||||
<field eval=""""Create Invoice"""" name="name"/>
|
||||
<field eval=""""The Salesman creates an invoice manually, if the sales order shipping policy is 'Shipping and Manual in Progress'. The invoice is created automatically if the shipping policy is 'Payment before Delivery'."""" name="note"/>
|
||||
<field model="process.node" name="target_node_id" ref="process_node_invoice0"/>
|
||||
<field model="process.node" name="source_node_id" ref="process_node_saleorder0"/>
|
||||
<field eval="[(6,0,[ref('sale.trans_wait_invoice_invoice_manual')])]" name="transition_ids"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_invoiceafterdelivery0" model="process.transition">
|
||||
<field eval="[(6,0,[])]" name="transition_ids"/>
|
||||
<field eval=""""Create Invoice"""" name="name"/>
|
||||
<field eval=""""The invoice is created automatically if the shipping policy is 'Invoice from pick' or 'Invoice on order after delivery'."""" name="note"/>
|
||||
<field model="process.node" name="target_node_id" ref="process_node_invoiceafterdelivery0"/>
|
||||
<field model="process.node" name="source_node_id" ref="process_node_packinglist0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_saleorderprocurement0" model="process.transition">
|
||||
<field eval="[(6,0,[])]" name="transition_ids"/>
|
||||
<field eval=""""Procurement of sold material"""" name="name"/>
|
||||
<field eval=""""For every sales order line, a procurement order is created to supply the sold product."""" name="note"/>
|
||||
<field model="process.node" name="target_node_id" ref="procurement.process_node_procureproducts0"/>
|
||||
<field model="process.node" name="source_node_id" ref="process_node_saleorderprocurement0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_saleinvoice0" model="process.transition">
|
||||
<field eval="[(6,0,[])]" name="transition_ids"/>
|
||||
|
@ -217,61 +125,6 @@
|
|||
<field name="transition_id" ref="process_transition_confirmquotation0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_assign0" model="process.transition.action">
|
||||
<field eval=""""action_assign"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Assign"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_packing0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_forceassignation0" model="process.transition.action">
|
||||
<field eval=""""force_assign"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Force Assignation"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_packing0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_cancel1" model="process.transition.action">
|
||||
<field eval=""""action_cancel"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Cancel"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_packing0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_cancelassignation0" model="process.transition.action">
|
||||
<field eval=""""cancel_assign"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Cancel Assignation"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_deliver0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_validate0" model="process.transition.action">
|
||||
<field eval=""""test_finished"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Validate"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_deliver0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_cancel2" model="process.transition.action">
|
||||
<field eval=""""action_cancel"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Cancel"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_deliver0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_createinvoice0" model="process.transition.action">
|
||||
<field eval=""""action_invoice_create"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Create Invoice"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_invoice0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_cancelorder0" model="process.transition.action">
|
||||
<field eval=""""action_cancel"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Cancel Order"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_invoice0"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -30,8 +30,6 @@ class sale_report(osv.osv):
|
|||
_columns = {
|
||||
'date': fields.date('Date Order', readonly=True),
|
||||
'date_confirm': fields.date('Date Confirm', readonly=True),
|
||||
'shipped': fields.boolean('Shipped', readonly=True),
|
||||
'shipped_qty_1': fields.integer('Shipped', readonly=True),
|
||||
'year': fields.char('Year', size=4, readonly=True),
|
||||
'month': fields.selection([('01', 'January'), ('02', 'February'), ('03', 'March'), ('04', 'April'),
|
||||
('05', 'May'), ('06', 'June'), ('07', 'July'), ('08', 'August'), ('09', 'September'),
|
||||
|
@ -54,7 +52,6 @@ class sale_report(osv.osv):
|
|||
('waiting_date', 'Waiting Schedule'),
|
||||
('manual', 'Manual In Progress'),
|
||||
('progress', 'In Progress'),
|
||||
('shipping_except', 'Shipping Exception'),
|
||||
('invoice_except', 'Invoice Exception'),
|
||||
('done', 'Done'),
|
||||
('cancel', 'Cancelled')
|
||||
|
@ -86,8 +83,6 @@ class sale_report(osv.osv):
|
|||
extract(epoch from avg(date_trunc('day',s.date_confirm)-date_trunc('day',s.create_date)))/(24*60*60)::decimal(16,2) as delay,
|
||||
s.state,
|
||||
t.categ_id as categ_id,
|
||||
s.shipped,
|
||||
s.shipped::integer as shipped_qty_1,
|
||||
s.pricelist_id as pricelist_id,
|
||||
s.project_id as analytic_account_id
|
||||
from
|
||||
|
@ -110,7 +105,6 @@ class sale_report(osv.osv):
|
|||
s.shop_id,
|
||||
s.company_id,
|
||||
s.state,
|
||||
s.shipped,
|
||||
s.pricelist_id,
|
||||
s.project_id
|
||||
)
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
<field name="categ_id" invisible="1"/>
|
||||
<field name="nbr" sum="# of Lines"/>
|
||||
<field name="product_uom_qty" sum="# of Qty"/>
|
||||
<field name="shipped_qty_1" sum="Shipped"/>
|
||||
<field name="product_uom" invisible="not context.get('set_visible',False)"/>
|
||||
<field name="price_total" sum="Total Price"/>
|
||||
<field name="delay" sum="Commitment Delay"/>
|
||||
|
|
|
@ -30,14 +30,6 @@ class sale_configuration(osv.osv_memory):
|
|||
'group_invoice_so_lines': fields.boolean('Based on Sale Orders',
|
||||
implied_group='sale.group_invoice_so_lines',
|
||||
help="To allow your salesman to make invoices for sale order lines using the menu 'Lines to Invoice'."),
|
||||
'group_invoice_deli_orders': fields.boolean('Based on Delivery Orders',
|
||||
implied_group='sale.group_invoice_deli_orders',
|
||||
help="To allow your salesman to make invoices for Delivery Orders using the menu 'Deliveries to Invoice'."),
|
||||
'task_work': fields.boolean('Based on Task Activities',
|
||||
help="""Lets you transfer the entries under tasks defined for Project Management to
|
||||
the Timesheet line entries for particular date and particular user with the effect of creating, editing and deleting either ways
|
||||
and to automatically creates project tasks from procurement lines.
|
||||
This installs the modules project_timesheet and project_mrp."""),
|
||||
'timesheet': fields.boolean('Based on Timesheet',
|
||||
help = """For modifying account analytic view to show important data to project manager of services companies.
|
||||
You can also view the report of account analytic summary user-wise as well as month wise.
|
||||
|
@ -49,16 +41,10 @@ class sale_configuration(osv.osv_memory):
|
|||
You will be able to follow the progress of the contract and invoice automatically.
|
||||
It installs the account_analytic_analysis module."""),
|
||||
'default_order_policy': fields.selection(
|
||||
[('manual', 'Invoice Based on Sales Orders'), ('picking', 'Invoice Based on Deliveries')],
|
||||
[('manual', 'Invoice Based on Sales Orders')],
|
||||
'Default Method', default_model='sale.order',
|
||||
help="You can generate invoices based on sales orders or based on shippings."),
|
||||
'module_delivery': fields.boolean('Allow Charging Shipping Costs',
|
||||
help ="""Allows you to add delivery methods in sale orders and delivery orders.
|
||||
You can define your own carrier and delivery grids for prices.
|
||||
This installs the module delivery."""),
|
||||
help="You can generate invoices based on sales orders."),
|
||||
'time_unit': fields.many2one('product.uom', 'Working Time Unit'),
|
||||
'default_picking_policy' : fields.boolean("Configurable Shipping Policy",
|
||||
help = "You will be able to configure, per sale order, if you deliver all products at once or if you deliver each product when it is available. This may have an impact on the shipping price."),
|
||||
'group_sale_pricelist':fields.boolean("Pricelist per Customer",
|
||||
implied_group='product.group_sale_pricelist',
|
||||
help="""Allows to manage different prices based on rules per category of customers.
|
||||
|
@ -69,15 +55,9 @@ class sale_configuration(osv.osv_memory):
|
|||
'group_sale_delivery_address': fields.boolean("Allow Different Addresses for Delivery and Invoice",
|
||||
implied_group='sale.group_delivery_invoice_address',
|
||||
help="Allows you to specify different delivery and invoice addresses on a sale order."),
|
||||
'group_mrp_properties': fields.boolean('Properties on Lines',
|
||||
implied_group='sale.group_mrp_properties',
|
||||
help="Allows you to tag sale order lines with properties."),
|
||||
'group_discount_per_so_line': fields.boolean("Discount per Line",
|
||||
implied_group='sale.group_discount_per_so_line',
|
||||
help="Allows you to apply some discount per sale order line."),
|
||||
'group_multiple_shops': fields.boolean("Manage Multiple Shops",
|
||||
implied_group='stock.group_locations',
|
||||
help="This allows to configure and use multiple shops."),
|
||||
'module_warning': fields.boolean("Alerts by Products or Customers",
|
||||
help="""Allow to configure warnings on products and trigger them when a user wants to sale a given product or a given customer.
|
||||
Example: Product: this product is deprecated, do not purchase more than 5.
|
||||
|
@ -95,8 +75,6 @@ class sale_configuration(osv.osv_memory):
|
|||
This is mostly used when a user encodes his timesheet. The values are retrieved and the fields are auto-filled.
|
||||
But the possibility to change these values is still available.
|
||||
This installs the module analytic_user_function."""),
|
||||
'module_project_timesheet': fields.boolean("Project Timesheet"),
|
||||
'module_project_mrp': fields.boolean("Project MRP"),
|
||||
'module_project': fields.boolean("Project"),
|
||||
'decimal_precision': fields.integer('Decimal Precision on Price',help="As an example, a decimal precision of 2 will allow prices like: 9.99 EUR, whereas a decimal precision of 4 will allow prices like: 0.0231 EUR per unit."),
|
||||
}
|
||||
|
@ -113,8 +91,6 @@ class sale_configuration(osv.osv_memory):
|
|||
def default_get(self, cr, uid, fields, context=None):
|
||||
ir_model_data = self.pool.get('ir.model.data')
|
||||
res = super(sale_configuration, self).default_get(cr, uid, fields, context)
|
||||
# task_work, time_unit depend on other fields
|
||||
res['task_work'] = res.get('module_project_mrp') and res.get('module_project_timesheet')
|
||||
if res.get('module_project'):
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context)
|
||||
res['time_unit'] = user.company_id.project_time_mode_id.id
|
||||
|
@ -123,13 +99,6 @@ class sale_configuration(osv.osv_memory):
|
|||
res['time_unit'] = product.uom_id.id
|
||||
return res
|
||||
|
||||
def get_default_sale_config(self, cr, uid, ids, context=None):
|
||||
ir_values = self.pool.get('ir.values')
|
||||
default_picking_policy = ir_values.get_default(cr, uid, 'sale.order', 'picking_policy')
|
||||
return {
|
||||
'default_picking_policy': default_picking_policy == 'one',
|
||||
}
|
||||
|
||||
def _get_default_time_unit(self, cr, uid, context=None):
|
||||
ids = self.pool.get('product.uom').search(cr, uid, [('name', '=', _('Hour'))], context=context)
|
||||
return ids and ids[0] or False
|
||||
|
@ -149,13 +118,9 @@ class sale_configuration(osv.osv_memory):
|
|||
dp.write({'digits': config.decimal_precision})
|
||||
|
||||
def set_sale_defaults(self, cr, uid, ids, context=None):
|
||||
ir_values = self.pool.get('ir.values')
|
||||
ir_model_data = self.pool.get('ir.model.data')
|
||||
wizard = self.browse(cr, uid, ids)[0]
|
||||
|
||||
default_picking_policy = 'one' if wizard.default_picking_policy else 'direct'
|
||||
ir_values.set_default(cr, uid, 'sale.order', 'picking_policy', default_picking_policy)
|
||||
|
||||
if wizard.time_unit:
|
||||
product = ir_model_data.get_object(cr, uid, 'product', 'product_consultant')
|
||||
product.write({'uom_id': wizard.time_unit.id, 'uom_po_id': wizard.time_unit.id})
|
||||
|
@ -166,11 +131,9 @@ class sale_configuration(osv.osv_memory):
|
|||
|
||||
return {}
|
||||
|
||||
def onchange_invoice_methods(self, cr, uid, ids, group_invoice_so_lines, group_invoice_deli_orders, context=None):
|
||||
if not group_invoice_deli_orders:
|
||||
def onchange_invoice_methods(self, cr, uid, ids, group_invoice_so_lines, context=None):
|
||||
if group_invoice_so_lines:
|
||||
return {'value': {'default_order_policy': 'manual'}}
|
||||
if not group_invoice_so_lines:
|
||||
return {'value': {'default_order_policy': 'picking'}}
|
||||
return {}
|
||||
|
||||
def onchange_task_work(self, cr, uid, ids, task_work, context=None):
|
||||
|
|
|
@ -9,29 +9,21 @@
|
|||
<field name="inherit_id" ref="base_setup.view_sale_config_settings"/>
|
||||
<field name="arch" type="xml">
|
||||
<group name="config_sale" position="inside">
|
||||
<field name="module_project_timesheet" invisible="1"/>
|
||||
<field name="module_project_mrp" invisible="1"/>
|
||||
<separator string="Sale Order" colspan="2"/>
|
||||
<field name="group_uom"/>
|
||||
<field name="group_sale_delivery_address"/>
|
||||
<field name="group_sale_pricelist"/>
|
||||
<field name="group_multiple_shops"/>
|
||||
<field name="module_warning"/>
|
||||
<field name="decimal_precision"/>
|
||||
<field name="default_picking_policy" attrs="{'invisible':[('group_invoice_deli_orders','=',False)]}"/>
|
||||
<field name="group_discount_per_so_line"/>
|
||||
<field name="group_mrp_properties"/>
|
||||
<field name="module_sale_margin"/>
|
||||
<field name="module_sale_journal"/>
|
||||
</group>
|
||||
<group name="config_other" position="inside">
|
||||
<separator string="Invoicing Policy" colspan="2" />
|
||||
<field name="group_invoice_so_lines" on_change="onchange_invoice_methods(group_invoice_so_lines, group_invoice_deli_orders)"/>
|
||||
<field name="group_invoice_deli_orders" on_change="onchange_invoice_methods(group_invoice_so_lines, group_invoice_deli_orders)"/>
|
||||
<field name="task_work" on_change="onchange_task_work(task_work)"/>
|
||||
<field name="timesheet" on_change="onchange_timesheet(timesheet)"/>
|
||||
<field name="default_order_policy" attrs="{'invisible':['|',('group_invoice_so_lines','=',False),('group_invoice_deli_orders','=',False)],'required': ['|',('group_invoice_so_lines','=',True),('group_invoice_deli_orders','=',True)]}"/>
|
||||
<field name="module_delivery"/>
|
||||
<field name="default_order_policy" attrs="{'invisible':[('group_invoice_so_lines','=',False)],'required': [('group_invoice_so_lines','=',True)]}"/>
|
||||
<field name="time_unit" domain="[('category_id.name','=','Working Time')]"
|
||||
attrs="{'invisible': [('task_work','=',False), ('module_account_analytic_analysis','=',False)],'required': ['|', ('task_work','=',True), ('module_account_analytic_analysis','=',True)]}"/>
|
||||
<separator string="Contracts" colspan="2"/>
|
||||
|
|
|
@ -35,7 +35,6 @@ class sale_shop(osv.osv):
|
|||
_columns = {
|
||||
'name': fields.char('Shop Name', size=64, required=True),
|
||||
'payment_default_id': fields.many2one('account.payment.term', 'Default Payment Term', required=True),
|
||||
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
|
||||
'pricelist_id': fields.many2one('product.pricelist', 'Pricelist'),
|
||||
'project_id': fields.many2one('account.analytic.account', 'Analytic Account', domain=[('parent_id', '!=', False)]),
|
||||
'company_id': fields.many2one('res.company', 'Company', required=False),
|
||||
|
@ -50,16 +49,13 @@ class sale_order(osv.osv):
|
|||
_name = "sale.order"
|
||||
_inherit = ['ir.needaction_mixin', 'mail.thread']
|
||||
_description = "Sales Order"
|
||||
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
if not default:
|
||||
default = {}
|
||||
default.update({
|
||||
'state': 'draft',
|
||||
'shipped': False,
|
||||
'invoice_ids': [],
|
||||
'picking_ids': [],
|
||||
'date_confirm': False,
|
||||
'name': self.pool.get('ir.sequence').get(cr, uid, 'sale.order'),
|
||||
})
|
||||
|
@ -90,44 +86,6 @@ class sale_order(osv.osv):
|
|||
res[order.id]['amount_total'] = res[order.id]['amount_untaxed'] + res[order.id]['amount_tax']
|
||||
return res
|
||||
|
||||
# This is False
|
||||
def _picked_rate(self, cr, uid, ids, name, arg, context=None):
|
||||
if not ids:
|
||||
return {}
|
||||
res = {}
|
||||
tmp = {}
|
||||
for id in ids:
|
||||
tmp[id] = {'picked': 0.0, 'total': 0.0}
|
||||
cr.execute('''SELECT
|
||||
p.sale_id as sale_order_id, sum(m.product_qty) as nbr, mp.state as procurement_state, m.state as move_state, p.type as picking_type
|
||||
FROM
|
||||
stock_move m
|
||||
LEFT JOIN
|
||||
stock_picking p on (p.id=m.picking_id)
|
||||
LEFT JOIN
|
||||
procurement_order mp on (mp.move_id=m.id)
|
||||
WHERE
|
||||
p.sale_id IN %s GROUP BY m.state, mp.state, p.sale_id, p.type''', (tuple(ids),))
|
||||
|
||||
for item in cr.dictfetchall():
|
||||
if item['move_state'] == 'cancel':
|
||||
continue
|
||||
|
||||
if item['picking_type'] == 'in':#this is a returned picking
|
||||
tmp[item['sale_order_id']]['total'] -= item['nbr'] or 0.0 # Deducting the return picking qty
|
||||
if item['procurement_state'] == 'done' or item['move_state'] == 'done':
|
||||
tmp[item['sale_order_id']]['picked'] -= item['nbr'] or 0.0
|
||||
else:
|
||||
tmp[item['sale_order_id']]['total'] += item['nbr'] or 0.0
|
||||
if item['procurement_state'] == 'done' or item['move_state'] == 'done':
|
||||
tmp[item['sale_order_id']]['picked'] += item['nbr'] or 0.0
|
||||
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
if order.shipped:
|
||||
res[order.id] = 100.0
|
||||
else:
|
||||
res[order.id] = tmp[order.id]['total'] and (100.0 * tmp[order.id]['picked'] / tmp[order.id]['total']) or 0.0
|
||||
return res
|
||||
|
||||
def _invoiced_rate(self, cursor, user, ids, name, arg, context=None):
|
||||
res = {}
|
||||
|
@ -208,11 +166,9 @@ class sale_order(osv.osv):
|
|||
('cancel', 'Cancelled'),
|
||||
('waiting_date', 'Waiting Schedule'),
|
||||
('progress', 'Sale Order'),
|
||||
('manual', 'Sale to Invoice'),
|
||||
('shipping_except', 'Shipping Exception'),
|
||||
('invoice_except', 'Invoice Exception'),
|
||||
('done', 'Done'),
|
||||
], 'Status', readonly=True, help="Gives the state of the quotation or sales order. \nThe exception state is automatically set when a cancel operation occurs in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception). \nThe 'Waiting Schedule' state is set when the invoice is confirmed but waiting for the scheduler to run on the order date.", select=True),
|
||||
], 'Status', readonly=True, help="Gives the state of the quotation or sales order. \nThe exception state is automatically set when a cancel operation occurs in the invoice validation (Invoice Exception). \nThe 'Waiting Schedule' state is set when the invoice is confirmed but waiting for the scheduler to run on the order date.", select=True),
|
||||
'date_order': fields.date('Date', required=True, readonly=True, select=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}),
|
||||
'create_date': fields.datetime('Creation Date', readonly=True, select=True, help="Date on which sales order is created."),
|
||||
'date_confirm': fields.date('Confirmation Date', readonly=True, select=True, help="Date on which sales order is confirmed."),
|
||||
|
@ -220,28 +176,16 @@ class sale_order(osv.osv):
|
|||
'partner_id': fields.many2one('res.partner', 'Customer', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, required=True, change_default=True, select=True),
|
||||
'partner_invoice_id': fields.many2one('res.partner', 'Invoice Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Invoice address for current sales order."),
|
||||
'partner_shipping_id': fields.many2one('res.partner', 'Shipping Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Shipping address for current sales order."),
|
||||
|
||||
'incoterm': fields.many2one('stock.incoterms', 'Incoterm', help="Incoterm which stands for 'International Commercial terms' implies its a series of sales terms which are used in the commercial transaction."),
|
||||
'picking_policy': fields.selection([('direct', 'Deliver each product when available'), ('one', 'Deliver all products at once')],
|
||||
'Shipping Policy', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
|
||||
help="""If you don't have enough stock available to deliver all at once, do you accept partial shipments or not?"""),
|
||||
'order_policy': fields.selection([
|
||||
('manual', 'On Demand'),
|
||||
('picking', 'On Delivery Order'),
|
||||
('prepaid', 'Before Delivery'),
|
||||
], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
|
||||
help="""This field controls how invoice and delivery operations are synchronized.
|
||||
- With 'On Demand', the invoice is created manually when needed.
|
||||
- With 'On Delivery Order', a draft invoice is generated after all pickings have been processed.
|
||||
- With 'Before Delivery', a draft invoice is created, and it must be paid before delivery."""),
|
||||
'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Pricelist for current sales order."),
|
||||
'project_id': fields.many2one('account.analytic.account', 'Contract/Analytic Account', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="The analytic account related to a sales order."),
|
||||
|
||||
'order_line': fields.one2many('sale.order.line', 'order_id', 'Order Lines', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}),
|
||||
'invoice_ids': fields.many2many('account.invoice', 'sale_order_invoice_rel', 'order_id', 'invoice_id', 'Invoices', readonly=True, help="This is the list of invoices that have been generated for this sales order. The same sales order may have been invoiced in several times (by line for example)."),
|
||||
'picking_ids': fields.one2many('stock.picking.out', 'sale_id', 'Related Picking', readonly=True, help="This is a list of delivery orders that has been generated for this sales order."),
|
||||
'shipped': fields.boolean('Delivered', readonly=True, help="It indicates that the sales order has been delivered. This field is updated only after the scheduler(s) have been launched."),
|
||||
'picked_rate': fields.function(_picked_rate, string='Picked', type='float'),
|
||||
'invoiced_rate': fields.function(_invoiced_rate, string='Invoiced', type='float'),
|
||||
'invoiced': fields.function(_invoiced, string='Paid',
|
||||
fnct_search=_invoiced_search, type='boolean', help="It indicates that an invoice has been paid."),
|
||||
|
@ -266,15 +210,14 @@ class sale_order(osv.osv):
|
|||
},
|
||||
multi='sums', help="The total amount."),
|
||||
|
||||
'invoice_quantity': fields.selection([('order', 'Ordered Quantities'), ('procurement', 'Shipped Quantities')], 'Invoice on', help="The sale order will automatically create the invoice proposition (draft invoice). Ordered and delivered quantities may not be the same. You have to choose if you want your invoice based on ordered or shipped quantities. If the product is a service, shipped quantities means hours spent on the associated tasks.", required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'invoice_quantity': fields.selection([('order', 'Ordered Quantities')], 'Invoice on', help="The sale order will automatically create the invoice proposition (draft invoice). Ordered and delivered quantities may not be the same. You have to choose if you want your invoice based on ordered or shipped quantities. If the product is a service, shipped quantities means hours spent on the associated tasks.", required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'payment_term': fields.many2one('account.payment.term', 'Payment Term'),
|
||||
'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position'),
|
||||
'company_id': fields.related('shop_id','company_id',type='many2one',relation='res.company',string='Company',store=True,readonly=True)
|
||||
}
|
||||
_defaults = {
|
||||
'picking_policy': 'direct',
|
||||
'date_order': fields.date.context_today,
|
||||
'order_policy': 'manual',
|
||||
'order_policy': 'prepaid',
|
||||
'state': 'draft',
|
||||
'user_id': lambda obj, cr, uid, context: uid,
|
||||
'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'sale.order'),
|
||||
|
@ -295,8 +238,7 @@ class sale_order(osv.osv):
|
|||
if s['state'] in ['draft', 'cancel']:
|
||||
unlink_ids.append(s['id'])
|
||||
else:
|
||||
raise osv.except_osv(_('Invalid action !'), _('In order to delete a confirmed sale order, you must cancel it before ! To cancel a sale order, you must first cancel related picking or delivery orders.'))
|
||||
|
||||
raise osv.except_osv(_('Invalid action !'), _('In order to delete a confirmed sale order, you must cancel it before !'))
|
||||
return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
|
||||
|
||||
def onchange_shop_id(self, cr, uid, ids, shop_id):
|
||||
|
@ -309,20 +251,6 @@ class sale_order(osv.osv):
|
|||
v['pricelist_id'] = shop.pricelist_id.id
|
||||
return {'value': v}
|
||||
|
||||
def action_cancel_draft(self, cr, uid, ids, context=None):
|
||||
if not len(ids):
|
||||
return False
|
||||
cr.execute('select id from sale_order_line where order_id IN %s and state=%s', (tuple(ids), 'cancel'))
|
||||
line_ids = map(lambda x: x[0], cr.fetchall())
|
||||
self.write(cr, uid, ids, {'state': 'draft', 'invoice_ids': [], 'shipped': 0})
|
||||
self.pool.get('sale.order.line').write(cr, uid, line_ids, {'invoiced': False, 'state': 'draft', 'invoice_lines': [(6, 0, [])]})
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
for inv_id in ids:
|
||||
# Deleting the existing instance of workflow for SO
|
||||
wf_service.trg_delete(uid, 'sale.order', inv_id, cr)
|
||||
wf_service.trg_create(uid, 'sale.order', inv_id, cr)
|
||||
self.action_cancel_draft_send_note(cr, uid, ids, context=context)
|
||||
return True
|
||||
|
||||
def onchange_pricelist_id(self, cr, uid, ids, pricelist_id, order_lines, context={}):
|
||||
if (not pricelist_id) or (not order_lines):
|
||||
|
@ -333,15 +261,6 @@ class sale_order(osv.osv):
|
|||
}
|
||||
return {'warning': warning}
|
||||
|
||||
def onchange_partner_order_id(self, cr, uid, ids, order_id, invoice_id=False, shipping_id=False, context={}):
|
||||
if not order_id:
|
||||
return {}
|
||||
val = {}
|
||||
if not invoice_id:
|
||||
val['partner_invoice_id'] = order_id
|
||||
if not shipping_id:
|
||||
val['partner_shipping_id'] = order_id
|
||||
return {'value': val}
|
||||
|
||||
def onchange_partner_id(self, cr, uid, ids, part):
|
||||
if not part:
|
||||
|
@ -364,30 +283,7 @@ class sale_order(osv.osv):
|
|||
val['pricelist_id'] = pricelist
|
||||
return {'value': val}
|
||||
|
||||
def shipping_policy_change(self, cr, uid, ids, policy, context=None):
|
||||
if not policy:
|
||||
return {}
|
||||
inv_qty = 'order'
|
||||
if policy == 'prepaid':
|
||||
inv_qty = 'order'
|
||||
elif policy == 'picking':
|
||||
inv_qty = 'procurement'
|
||||
return {'value': {'invoice_quantity': inv_qty}}
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
if vals.get('order_policy', False):
|
||||
if vals['order_policy'] == 'prepaid':
|
||||
vals.update({'invoice_quantity': 'order'})
|
||||
elif vals['order_policy'] == 'picking':
|
||||
vals.update({'invoice_quantity': 'procurement'})
|
||||
return super(sale_order, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
if vals.get('order_policy', False):
|
||||
if vals['order_policy'] == 'prepaid':
|
||||
vals.update({'invoice_quantity': 'order'})
|
||||
if vals['order_policy'] == 'picking':
|
||||
vals.update({'invoice_quantity': 'procurement'})
|
||||
order = super(sale_order, self).create(cr, uid, vals, context=context)
|
||||
if order:
|
||||
self.create_send_note(cr, uid, [order], context=context)
|
||||
|
@ -547,46 +443,10 @@ class sale_order(osv.osv):
|
|||
result.update(view_id = res and res[1] or False)
|
||||
return result
|
||||
|
||||
|
||||
def action_view_delivery(self, cr, uid, ids, context=None):
|
||||
'''
|
||||
This function returns an action that display existing delivery orders of given sale order ids. It can either be a in a list or in a form view, if there is only one delivery order to show.
|
||||
'''
|
||||
mod_obj = self.pool.get('ir.model.data')
|
||||
result = {
|
||||
'name': _('Delivery Order'),
|
||||
'view_type': 'form',
|
||||
'res_model': 'stock.picking',
|
||||
'context': "{'type':'out'}",
|
||||
'type': 'ir.actions.act_window',
|
||||
'nodestroy': True,
|
||||
'target': 'current',
|
||||
}
|
||||
#compute the number of delivery orders to display
|
||||
pick_ids = []
|
||||
for so in self.browse(cr, uid, ids, context=context):
|
||||
pick_ids += [picking.id for picking in so.picking_ids]
|
||||
#choose the view_mode accordingly
|
||||
if len(pick_ids) > 1:
|
||||
res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_tree')
|
||||
result.update({
|
||||
'view_mode': 'tree,form',
|
||||
'res_id': pick_ids or False
|
||||
})
|
||||
else:
|
||||
res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_form')
|
||||
result.update({
|
||||
'view_mode': 'form',
|
||||
'res_id': pick_ids and pick_ids[0] or False,
|
||||
})
|
||||
result.update(view_id = res and res[1] or False)
|
||||
return result
|
||||
|
||||
def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed', 'done', 'exception'], date_inv = False, context=None):
|
||||
res = False
|
||||
invoices = {}
|
||||
invoice_ids = []
|
||||
picking_obj = self.pool.get('stock.picking')
|
||||
invoice = self.pool.get('account.invoice')
|
||||
obj_sale_order_line = self.pool.get('sale.order.line')
|
||||
partner_currency = {}
|
||||
|
@ -625,8 +485,6 @@ class sale_order(osv.osv):
|
|||
for o, l in val:
|
||||
invoice_ref += o.name + '|'
|
||||
self.write(cr, uid, [o.id], {'state': 'progress'})
|
||||
if o.order_policy == 'picking':
|
||||
picking_obj.write(cr, uid, map(lambda x: x.id, o.picking_ids), {'invoice_state': 'invoiced'})
|
||||
cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (o.id, res))
|
||||
invoice.write(cr, uid, [res], {'origin': invoice_ref, 'name': invoice_ref})
|
||||
else:
|
||||
|
@ -634,8 +492,6 @@ class sale_order(osv.osv):
|
|||
res = self._make_invoice(cr, uid, order, il, context=context)
|
||||
invoice_ids.append(res)
|
||||
self.write(cr, uid, [order.id], {'state': 'progress'})
|
||||
if order.order_policy == 'picking':
|
||||
picking_obj.write(cr, uid, map(lambda x: x.id, order.picking_ids), {'invoice_state': 'invoiced'})
|
||||
cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (order.id, res))
|
||||
if res:
|
||||
self.invoice_send_note(cr, uid, ids, res, context)
|
||||
|
@ -697,22 +553,7 @@ class sale_order(osv.osv):
|
|||
if context is None:
|
||||
context = {}
|
||||
sale_order_line_obj = self.pool.get('sale.order.line')
|
||||
proc_obj = self.pool.get('procurement.order')
|
||||
for sale in self.browse(cr, uid, ids, context=context):
|
||||
for pick in sale.picking_ids:
|
||||
if pick.state not in ('draft', 'cancel'):
|
||||
raise osv.except_osv(
|
||||
_('Could not cancel sales order !'),
|
||||
_('You must first cancel all picking attached to this sales order.'))
|
||||
if pick.state == 'cancel':
|
||||
for mov in pick.move_lines:
|
||||
proc_ids = proc_obj.search(cr, uid, [('move_id', '=', mov.id)])
|
||||
if proc_ids:
|
||||
for proc in proc_ids:
|
||||
wf_service.trg_validate(uid, 'procurement.order', proc, 'button_check', cr)
|
||||
for r in self.read(cr, uid, ids, ['picking_ids']):
|
||||
for pick in r['picking_ids']:
|
||||
wf_service.trg_validate(uid, 'stock.picking', pick, 'button_cancel', cr)
|
||||
for inv in sale.invoice_ids:
|
||||
if inv.state not in ('draft', 'cancel'):
|
||||
raise osv.except_osv(
|
||||
|
@ -751,10 +592,7 @@ class sale_order(osv.osv):
|
|||
for o in self.browse(cr, uid, ids):
|
||||
if not o.order_line:
|
||||
raise osv.except_osv(_('Error !'),_('You cannot confirm a sale order which has no line.'))
|
||||
if (o.order_policy == 'manual'):
|
||||
self.write(cr, uid, [o.id], {'state': 'manual', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})
|
||||
else:
|
||||
self.write(cr, uid, [o.id], {'state': 'progress', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})
|
||||
self.write(cr, uid, [o.id], {'state': 'progress', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})
|
||||
self.pool.get('sale.order.line').button_confirm(cr, uid, [x.id for x in o.order_line])
|
||||
self.confirm_send_note(cr, uid, ids, context)
|
||||
return True
|
||||
|
@ -783,241 +621,6 @@ class sale_order(osv.osv):
|
|||
'nodestroy': True,
|
||||
}
|
||||
|
||||
def procurement_lines_get(self, cr, uid, ids, *args):
|
||||
res = []
|
||||
for order in self.browse(cr, uid, ids, context={}):
|
||||
for line in order.order_line:
|
||||
if line.procurement_id:
|
||||
res.append(line.procurement_id.id)
|
||||
return res
|
||||
|
||||
# if mode == 'finished':
|
||||
# returns True if all lines are done, False otherwise
|
||||
# if mode == 'canceled':
|
||||
# returns True if there is at least one canceled line, False otherwise
|
||||
def test_state(self, cr, uid, ids, mode, *args):
|
||||
assert mode in ('finished', 'canceled'), _("invalid mode for test_state")
|
||||
finished = True
|
||||
canceled = False
|
||||
notcanceled = False
|
||||
write_done_ids = []
|
||||
write_cancel_ids = []
|
||||
for order in self.browse(cr, uid, ids, context={}):
|
||||
for line in order.order_line:
|
||||
if (not line.procurement_id) or (line.procurement_id.state=='done'):
|
||||
if line.state != 'done':
|
||||
write_done_ids.append(line.id)
|
||||
else:
|
||||
finished = False
|
||||
if line.procurement_id:
|
||||
if (line.procurement_id.state == 'cancel'):
|
||||
canceled = True
|
||||
if line.state != 'exception':
|
||||
write_cancel_ids.append(line.id)
|
||||
else:
|
||||
notcanceled = True
|
||||
if write_done_ids:
|
||||
self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'})
|
||||
if write_cancel_ids:
|
||||
self.pool.get('sale.order.line').write(cr, uid, write_cancel_ids, {'state': 'exception'})
|
||||
|
||||
if mode == 'finished':
|
||||
return finished
|
||||
elif mode == 'canceled':
|
||||
return canceled
|
||||
if notcanceled:
|
||||
return False
|
||||
return canceled
|
||||
|
||||
def _prepare_order_line_procurement(self, cr, uid, order, line, move_id, date_planned, context=None):
|
||||
return {
|
||||
'name': line.name.split('\n')[0],
|
||||
'origin': order.name,
|
||||
'date_planned': date_planned,
|
||||
'product_id': line.product_id.id,
|
||||
'product_qty': line.product_uom_qty,
|
||||
'product_uom': line.product_uom.id,
|
||||
'product_uos_qty': (line.product_uos and line.product_uos_qty)\
|
||||
or line.product_uom_qty,
|
||||
'product_uos': (line.product_uos and line.product_uos.id)\
|
||||
or line.product_uom.id,
|
||||
'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
|
||||
'procure_method': line.type,
|
||||
'move_id': move_id,
|
||||
'company_id': order.company_id.id,
|
||||
'note': '\n'.join(line.name.split('\n')[1:]),
|
||||
'property_ids': [(6, 0, [x.id for x in line.property_ids])]
|
||||
}
|
||||
|
||||
def _prepare_order_line_move(self, cr, uid, order, line, picking_id, date_planned, context=None):
|
||||
location_id = order.shop_id.warehouse_id.lot_stock_id.id
|
||||
output_id = order.shop_id.warehouse_id.lot_output_id.id
|
||||
return {
|
||||
'name': line.name.split('\n')[0][:250],
|
||||
'picking_id': picking_id,
|
||||
'product_id': line.product_id.id,
|
||||
'date': date_planned,
|
||||
'date_expected': date_planned,
|
||||
'product_qty': line.product_uom_qty,
|
||||
'product_uom': line.product_uom.id,
|
||||
'product_uos_qty': (line.product_uos and line.product_uos_qty) or line.product_uom_qty,
|
||||
'product_uos': (line.product_uos and line.product_uos.id)\
|
||||
or line.product_uom.id,
|
||||
'product_packaging': line.product_packaging.id,
|
||||
'partner_id': line.address_allotment_id.id or order.partner_shipping_id.id,
|
||||
'location_id': location_id,
|
||||
'location_dest_id': output_id,
|
||||
'sale_line_id': line.id,
|
||||
'tracking_id': False,
|
||||
'state': 'draft',
|
||||
#'state': 'waiting',
|
||||
'note': '\n'.join(line.name.split('\n')[1:]),
|
||||
'company_id': order.company_id.id,
|
||||
'price_unit': line.product_id.standard_price or 0.0
|
||||
}
|
||||
|
||||
def _prepare_order_picking(self, cr, uid, order, context=None):
|
||||
pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out')
|
||||
return {
|
||||
'name': pick_name,
|
||||
'origin': order.name,
|
||||
'date': order.date_order,
|
||||
'type': 'out',
|
||||
'state': 'auto',
|
||||
'move_type': order.picking_policy,
|
||||
'sale_id': order.id,
|
||||
'partner_id': order.partner_shipping_id.id,
|
||||
'note': order.note,
|
||||
'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none',
|
||||
'company_id': order.company_id.id,
|
||||
}
|
||||
|
||||
def ship_recreate(self, cr, uid, order, line, move_id, proc_id):
|
||||
# FIXME: deals with potentially cancelled shipments, seems broken (specially if shipment has production lot)
|
||||
"""
|
||||
Define ship_recreate for process after shipping exception
|
||||
param order: sale order to which the order lines belong
|
||||
param line: sale order line records to procure
|
||||
param move_id: the ID of stock move
|
||||
param proc_id: the ID of procurement
|
||||
"""
|
||||
move_obj = self.pool.get('stock.move')
|
||||
if order.state == 'shipping_except':
|
||||
for pick in order.picking_ids:
|
||||
for move in pick.move_lines:
|
||||
if move.state == 'cancel':
|
||||
mov_ids = move_obj.search(cr, uid, [('state', '=', 'cancel'),('sale_line_id', '=', line.id),('picking_id', '=', pick.id)])
|
||||
if mov_ids:
|
||||
for mov in move_obj.browse(cr, uid, mov_ids):
|
||||
# FIXME: the following seems broken: what if move_id doesn't exist? What if there are several mov_ids? Shouldn't that be a sum?
|
||||
move_obj.write(cr, uid, [move_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty})
|
||||
self.pool.get('procurement.order').write(cr, uid, [proc_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty})
|
||||
return True
|
||||
|
||||
def _get_date_planned(self, cr, uid, order, line, start_date, context=None):
|
||||
date_planned = datetime.strptime(start_date, DEFAULT_SERVER_DATE_FORMAT) + relativedelta(days=line.delay or 0.0)
|
||||
date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
return date_planned
|
||||
|
||||
def _create_pickings_and_procurements(self, cr, uid, order, order_lines, picking_id=False, context=None):
|
||||
"""Create the required procurements to supply sale order lines, also connecting
|
||||
the procurements to appropriate stock moves in order to bring the goods to the
|
||||
sale order's requested location.
|
||||
|
||||
If ``picking_id`` is provided, the stock moves will be added to it, otherwise
|
||||
a standard outgoing picking will be created to wrap the stock moves, as returned
|
||||
by :meth:`~._prepare_order_picking`.
|
||||
|
||||
Modules that wish to customize the procurements or partition the stock moves over
|
||||
multiple stock pickings may override this method and call ``super()`` with
|
||||
different subsets of ``order_lines`` and/or preset ``picking_id`` values.
|
||||
|
||||
:param browse_record order: sale order to which the order lines belong
|
||||
:param list(browse_record) order_lines: sale order line records to procure
|
||||
:param int picking_id: optional ID of a stock picking to which the created stock moves
|
||||
will be added. A new picking will be created if ommitted.
|
||||
:return: True
|
||||
"""
|
||||
move_obj = self.pool.get('stock.move')
|
||||
picking_obj = self.pool.get('stock.picking')
|
||||
procurement_obj = self.pool.get('procurement.order')
|
||||
proc_ids = []
|
||||
|
||||
for line in order_lines:
|
||||
if line.state == 'done':
|
||||
continue
|
||||
|
||||
date_planned = self._get_date_planned(cr, uid, order, line, order.date_order, context=context)
|
||||
|
||||
if line.product_id:
|
||||
if line.product_id.product_tmpl_id.type in ('product', 'consu'):
|
||||
if not picking_id:
|
||||
picking_id = picking_obj.create(cr, uid, self._prepare_order_picking(cr, uid, order, context=context))
|
||||
move_id = move_obj.create(cr, uid, self._prepare_order_line_move(cr, uid, order, line, picking_id, date_planned, context=context))
|
||||
else:
|
||||
# a service has no stock move
|
||||
move_id = False
|
||||
|
||||
proc_id = procurement_obj.create(cr, uid, self._prepare_order_line_procurement(cr, uid, order, line, move_id, date_planned, context=context))
|
||||
proc_ids.append(proc_id)
|
||||
line.write({'procurement_id': proc_id})
|
||||
self.ship_recreate(cr, uid, order, line, move_id, proc_id)
|
||||
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
if picking_id:
|
||||
wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
|
||||
self.delivery_send_note(cr, uid, [order.id], picking_id, context)
|
||||
|
||||
|
||||
for proc_id in proc_ids:
|
||||
wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr)
|
||||
|
||||
val = {}
|
||||
if order.state == 'shipping_except':
|
||||
val['state'] = 'progress'
|
||||
val['shipped'] = False
|
||||
|
||||
if (order.order_policy == 'manual'):
|
||||
for line in order.order_line:
|
||||
if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
|
||||
val['state'] = 'manual'
|
||||
break
|
||||
order.write(val)
|
||||
return True
|
||||
|
||||
def action_ship_create(self, cr, uid, ids, context=None):
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
self._create_pickings_and_procurements(cr, uid, order, order.order_line, None, context=context)
|
||||
return True
|
||||
|
||||
def action_ship_end(self, cr, uid, ids, context=None):
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
val = {'shipped': True}
|
||||
if order.state == 'shipping_except':
|
||||
val['state'] = 'progress'
|
||||
if (order.order_policy == 'manual'):
|
||||
for line in order.order_line:
|
||||
if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
|
||||
val['state'] = 'manual'
|
||||
break
|
||||
for line in order.order_line:
|
||||
towrite = []
|
||||
if line.state == 'exception':
|
||||
towrite.append(line.id)
|
||||
if towrite:
|
||||
self.pool.get('sale.order.line').write(cr, uid, towrite, {'state': 'done'}, context=context)
|
||||
res = self.write(cr, uid, [order.id], val)
|
||||
if res:
|
||||
self.delivery_end_send_note(cr, uid, [order.id], context=context)
|
||||
return True
|
||||
|
||||
def has_stockable_products(self, cr, uid, ids, *args):
|
||||
for order in self.browse(cr, uid, ids):
|
||||
for order_line in order.order_line:
|
||||
if order_line.product_id and order_line.product_id.product_tmpl_id.type in ('product', 'consu'):
|
||||
return True
|
||||
return False
|
||||
|
||||
# ------------------------------------------------
|
||||
# OpenChatter methods and notifications
|
||||
# ------------------------------------------------
|
||||
|
@ -1025,7 +628,7 @@ class sale_order(osv.osv):
|
|||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = super(sale_order, self).get_needaction_user_ids(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if (obj.state == 'manual' or obj.state == 'progress'):
|
||||
if (obj.state == 'progress'):
|
||||
result[obj.id].append(obj.user_id.id)
|
||||
return result
|
||||
|
||||
|
@ -1042,18 +645,7 @@ class sale_order(osv.osv):
|
|||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
self.message_append_note(cr, uid, [obj.id], body=_("Sale Order for <em>%s</em> <b>cancelled</b>.") % (obj.partner_id.name), context=context)
|
||||
|
||||
def delivery_send_note(self, cr, uid, ids, picking_id, context=None):
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
for picking in (pck for pck in order.picking_ids if pck.id == picking_id):
|
||||
# convert datetime field to a datetime, using server format, then
|
||||
# convert it to the user TZ and re-render it with %Z to add the timezone
|
||||
picking_datetime = fields.DT.datetime.strptime(picking.min_date, DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
picking_date_str = fields.datetime.context_timestamp(cr, uid, picking_datetime, context=context).strftime(DATETIME_FORMATS_MAP['%+'] + " (%Z)")
|
||||
self.message_append_note(cr, uid, [order.id], body=_("Delivery Order <em>%s</em> <b>scheduled</b> for %s.") % (picking.name, picking_date_str), context=context)
|
||||
|
||||
def delivery_end_send_note(self, cr, uid, ids, context=None):
|
||||
self.message_append_note(cr, uid, ids, body=_("Order <b>delivered</b>."), context=context)
|
||||
|
||||
|
||||
def invoice_paid_send_note(self, cr, uid, ids, context=None):
|
||||
self.message_append_note(cr, uid, ids, body=_("Invoice <b>paid</b>."), context=context)
|
||||
|
||||
|
@ -1062,9 +654,6 @@ class sale_order(osv.osv):
|
|||
for invoice in (inv for inv in order.invoice_ids if inv.id == invoice_id):
|
||||
self.message_append_note(cr, uid, [order.id], body=_("Draft Invoice of %s %s <b>waiting for validation</b>.") % (invoice.amount_total, invoice.currency_id.symbol), context=context)
|
||||
|
||||
def action_cancel_draft_send_note(self, cr, uid, ids, context=None):
|
||||
return self.message_append_note(cr, uid, ids, body='Sale order has been set in draft.', context=context)
|
||||
|
||||
|
||||
sale_order()
|
||||
|
||||
|
@ -1086,14 +675,7 @@ class sale_order_line(osv.osv):
|
|||
res[line.id] = cur_obj.round(cr, uid, cur, taxes['total'])
|
||||
return res
|
||||
|
||||
def _number_packages(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = {}
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
try:
|
||||
res[line.id] = int((line.product_uom_qty+line.product_packaging.qty-0.0001) / line.product_packaging.qty)
|
||||
except:
|
||||
res[line.id] = 1
|
||||
return res
|
||||
|
||||
|
||||
def _get_uom_id(self, cr, uid, *args):
|
||||
try:
|
||||
|
@ -1109,26 +691,18 @@ class sale_order_line(osv.osv):
|
|||
'order_id': fields.many2one('sale.order', 'Order Reference', required=True, ondelete='cascade', select=True, readonly=True, states={'draft':[('readonly',False)]}),
|
||||
'name': fields.text('Product Description', size=256, required=True, select=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of sales order lines."),
|
||||
'delay': fields.float('Delivery Lead Time', required=True, help="Number of days between the order confirmation the shipping of the products to the customer", readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True),
|
||||
'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True),
|
||||
'invoiced': fields.boolean('Invoiced', readonly=True),
|
||||
'procurement_id': fields.many2one('procurement.order', 'Procurement'),
|
||||
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Sale Price'), readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute= dp.get_precision('Sale Price')),
|
||||
'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes', readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'type': fields.selection([('make_to_stock', 'from stock'), ('make_to_order', 'on order')], 'Procurement Method', required=True, readonly=True, states={'draft': [('readonly', False)]},
|
||||
help="If 'on order', it triggers a procurement when the sale order is confirmed to create a task, purchase order or manufacturing order linked to this sale order line."),
|
||||
'property_ids': fields.many2many('mrp.property', 'sale_order_line_property_rel', 'order_id', 'property_id', 'Properties', readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'address_allotment_id': fields.many2one('res.partner', 'Allotment Partner'),
|
||||
'product_uom_qty': fields.float('Quantity', digits_compute= dp.get_precision('Product UoS'), required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'product_uom': fields.many2one('product.uom', 'Unit of Measure ', required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'product_uos_qty': fields.float('Quantity (UoS)' ,digits_compute= dp.get_precision('Product UoS'), readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'product_uos': fields.many2one('product.uom', 'Product UoS'),
|
||||
'product_packaging': fields.many2one('product.packaging', 'Packaging'),
|
||||
'move_ids': fields.one2many('stock.move', 'sale_line_id', 'Inventory Moves', readonly=True),
|
||||
'discount': fields.float('Discount', digits=(16, 2), readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'number_packages': fields.function(_number_packages, type='integer', string='Number Packages'),
|
||||
'th_weight': fields.float('Weight', readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'state': fields.selection([('cancel', 'Cancelled'),('draft', 'Draft'),('confirmed', 'Confirmed'),('exception', 'Exception'),('done', 'Done')], 'Status', required=True, readonly=True,
|
||||
help='* The \'Draft\' state is set when the related sales order in draft state. \
|
||||
|
@ -1144,14 +718,11 @@ class sale_order_line(osv.osv):
|
|||
_defaults = {
|
||||
'product_uom' : _get_uom_id,
|
||||
'discount': 0.0,
|
||||
'delay': 0.0,
|
||||
'product_uom_qty': 1,
|
||||
'product_uos_qty': 1,
|
||||
'sequence': 10,
|
||||
'invoiced': 0,
|
||||
'state': 'draft',
|
||||
'type': 'make_to_stock',
|
||||
'product_packaging': False,
|
||||
'price_unit': 0.0,
|
||||
}
|
||||
|
||||
|
@ -1168,22 +739,16 @@ class sale_order_line(osv.osv):
|
|||
"""
|
||||
|
||||
def _get_line_qty(line):
|
||||
if (line.order_id.invoice_quantity=='order') or not line.procurement_id:
|
||||
if (line.order_id.invoice_quantity=='order'):
|
||||
if line.product_uos:
|
||||
return line.product_uos_qty or 0.0
|
||||
return line.product_uom_qty
|
||||
else:
|
||||
return self.pool.get('procurement.order').quantity_get(cr, uid,
|
||||
line.procurement_id.id, context=context)
|
||||
|
||||
def _get_line_uom(line):
|
||||
if (line.order_id.invoice_quantity=='order') or not line.procurement_id:
|
||||
if (line.order_id.invoice_quantity=='order'):
|
||||
if line.product_uos:
|
||||
return line.product_uos.id
|
||||
return line.product_uom.id
|
||||
else:
|
||||
return self.pool.get('procurement.order').uom_get(cr, uid,
|
||||
line.procurement_id.id, context=context)
|
||||
|
||||
if not line.invoiced:
|
||||
if not account_id:
|
||||
|
@ -1293,52 +858,6 @@ class sale_order_line(osv.osv):
|
|||
default.update({'state': 'draft', 'move_ids': [], 'invoiced': False, 'invoice_lines': []})
|
||||
return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context)
|
||||
|
||||
def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False,
|
||||
partner_id=False, packaging=False, flag=False, context=None):
|
||||
if not product:
|
||||
return {'value': {'product_packaging': False}}
|
||||
product_obj = self.pool.get('product.product')
|
||||
product_uom_obj = self.pool.get('product.uom')
|
||||
pack_obj = self.pool.get('product.packaging')
|
||||
warning = {}
|
||||
result = {}
|
||||
warning_msgs = ''
|
||||
if flag:
|
||||
res = self.product_id_change(cr, uid, ids, pricelist=pricelist,
|
||||
product=product, qty=qty, uom=uom, partner_id=partner_id,
|
||||
packaging=packaging, flag=False, context=context)
|
||||
warning_msgs = res.get('warning') and res['warning']['message']
|
||||
|
||||
products = product_obj.browse(cr, uid, product, context=context)
|
||||
if not products.packaging:
|
||||
packaging = result['product_packaging'] = False
|
||||
elif not packaging and products.packaging and not flag:
|
||||
packaging = products.packaging[0].id
|
||||
result['product_packaging'] = packaging
|
||||
|
||||
if packaging:
|
||||
default_uom = products.uom_id and products.uom_id.id
|
||||
pack = pack_obj.browse(cr, uid, packaging, context=context)
|
||||
q = product_uom_obj._compute_qty(cr, uid, uom, pack.qty, default_uom)
|
||||
# qty = qty - qty % q + q
|
||||
if qty and (q and not (qty % q) == 0):
|
||||
ean = pack.ean or _('(n/a)')
|
||||
qty_pack = pack.qty
|
||||
type_ul = pack.ul
|
||||
if not warning_msgs:
|
||||
warn_msg = _("You selected a quantity of %d Units.\n"
|
||||
"But it's not compatible with the selected packaging.\n"
|
||||
"Here is a proposition of quantities according to the packaging:\n"
|
||||
"EAN: %s Quantity: %s Type of ul: %s") % \
|
||||
(qty, ean, qty_pack, type_ul.name)
|
||||
warning_msgs += _("Picking Information ! : ") + warn_msg + "\n\n"
|
||||
warning = {
|
||||
'title': _('Configuration Error !'),
|
||||
'message': warning_msgs
|
||||
}
|
||||
result['product_uom_qty'] = qty
|
||||
|
||||
return {'value': result, 'warning': warning}
|
||||
|
||||
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
|
||||
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
<!-- Resource: sale.shop -->
|
||||
<record id="shop" model="sale.shop">
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="warehouse_id" ref="stock.warehouse0"/>
|
||||
<field name="payment_default_id" ref="account.account_payment_term"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
|
||||
|
|
|
@ -11,9 +11,6 @@
|
|||
<field ref="base.res_partner_agrolait" name="partner_id"/>
|
||||
<field ref="base.res_partner_address_8" name="partner_invoice_id"/>
|
||||
<field ref="base.res_partner_address_8" name="partner_shipping_id"/>
|
||||
<field name="order_policy">picking</field>
|
||||
<field name="invoice_quantity">procurement</field>
|
||||
<field name="note">Invoice after delivery</field>
|
||||
</record>
|
||||
|
||||
<!--Resource: sale.order.line-->
|
||||
|
@ -24,7 +21,6 @@
|
|||
<field name="product_id" ref="product.product_product_mb1"/>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">123.20</field>
|
||||
<field name="type">make_to_stock</field>
|
||||
</record>
|
||||
|
||||
<record id="line1" model="sale.order.line">
|
||||
|
@ -35,7 +31,6 @@
|
|||
<field name="price_unit">450.50</field>
|
||||
<field name="product_uom_qty">3</field>
|
||||
<field name="product_uos_qty">3</field>
|
||||
<field name="type">make_to_stock</field>
|
||||
</record>
|
||||
<record id="line13" model="sale.order.line">
|
||||
<field name="order_id" ref="order"/>
|
||||
|
@ -44,8 +39,6 @@
|
|||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">900</field>
|
||||
<field name="product_uom_qty">5</field>
|
||||
<field name="type">make_to_stock</field>
|
||||
<field name="delay">1</field>
|
||||
</record>
|
||||
<record id="line12" model="sale.order.line">
|
||||
<field name="order_id" ref="order"/>
|
||||
|
@ -55,7 +48,6 @@
|
|||
<field name="price_unit">88</field>
|
||||
<field name="product_uom_qty">5</field>
|
||||
<field name="product_uos_qty">5</field>
|
||||
<field name="type">make_to_stock</field>
|
||||
</record>
|
||||
|
||||
<!--Resource: sale.order-->
|
||||
|
@ -80,7 +72,6 @@
|
|||
<field name="product_id" ref="product.product_product_pc2"/>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">540</field>
|
||||
<field name="type">make_to_order</field>
|
||||
</record>
|
||||
<record id="line4" model="sale.order.line">
|
||||
<field name="order_id" ref="order2"/>
|
||||
|
@ -90,7 +81,6 @@
|
|||
<field name="price_unit">88</field>
|
||||
<field name="product_uom_qty">8</field>
|
||||
<field name="product_uos_qty">8</field>
|
||||
<field name="type">make_to_order</field>
|
||||
</record>
|
||||
|
||||
<!--Resource: sale.order-->
|
||||
|
@ -111,8 +101,6 @@
|
|||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="product_uom_qty">3</field>
|
||||
<field name="price_unit">450</field>
|
||||
<field name="type">make_to_order</field>
|
||||
<field name="delay">2</field>
|
||||
</record>
|
||||
<record id="line6" model="sale.order.line">
|
||||
<field name="order_id" ref="order3"/>
|
||||
|
@ -121,8 +109,6 @@
|
|||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">750</field>
|
||||
<field name="product_uom_qty">3</field>
|
||||
<field name="type">make_to_order</field>
|
||||
<field name="delay">7</field>
|
||||
</record>
|
||||
|
||||
<!--Resource: sale.order-->
|
||||
|
@ -142,8 +128,6 @@
|
|||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="product_uom_qty">2</field>
|
||||
<field name="price_unit">450</field>
|
||||
<field name="type">make_to_order</field>
|
||||
<field name="delay">8</field>
|
||||
</record>
|
||||
<record id="line8" model="sale.order.line">
|
||||
<field name="order_id" ref="order4"/>
|
||||
|
@ -152,10 +136,7 @@
|
|||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">900</field>
|
||||
<field name="product_uom_qty">5</field>
|
||||
<field name="type">make_to_order</field>
|
||||
<field name="delay">1</field>
|
||||
</record>
|
||||
<workflow action="order_confirm" model="sale.order" ref="order4"/>
|
||||
|
||||
<!--Resource: sale.order-->
|
||||
|
||||
|
@ -174,8 +155,6 @@
|
|||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="product_uom_qty">4</field>
|
||||
<field name="price_unit">750</field>
|
||||
<field name="type">make_to_order</field>
|
||||
<field name="delay">15</field>
|
||||
</record>
|
||||
<record id="line52" model="sale.order.line">
|
||||
<field name="order_id" ref="order5"/>
|
||||
|
@ -185,9 +164,7 @@
|
|||
<field name="price_unit">75</field>
|
||||
<field name="product_uom_qty">3</field>
|
||||
<field name="product_uos_qty">3</field>
|
||||
<field name="type">make_to_stock</field>
|
||||
</record>
|
||||
<workflow action="order_confirm" model="sale.order" ref="order5"/>
|
||||
|
||||
<!--Resource: sale.order-->
|
||||
|
||||
|
@ -206,8 +183,6 @@
|
|||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="product_uom_qty">4</field>
|
||||
<field name="price_unit">250</field>
|
||||
<field name="type">make_to_order</field>
|
||||
<field name="delay">15</field>
|
||||
</record>
|
||||
<record id="order6_line1" model="sale.order.line">
|
||||
<field name="order_id" ref="order6"/>
|
||||
|
@ -216,11 +191,7 @@
|
|||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="product_uom_qty">4</field>
|
||||
<field name="price_unit">500</field>
|
||||
<field name="type">make_to_order</field>
|
||||
<field name="delay">15</field>
|
||||
</record>
|
||||
<workflow action="order_confirm" model="sale.order" ref="order6"/>
|
||||
<workflow action="manual_invoice" model="sale.order" ref="order6"/>
|
||||
|
||||
<!--Resource: sale.order-->
|
||||
|
||||
|
@ -239,8 +210,6 @@
|
|||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="product_uom_qty">15</field>
|
||||
<field name="price_unit">250</field>
|
||||
<field name="type">make_to_stock</field>
|
||||
<field name="delay">15</field>
|
||||
</record>
|
||||
<record id="order7_line1" model="sale.order.line">
|
||||
<field name="order_id" ref="order7"/>
|
||||
|
@ -249,12 +218,8 @@
|
|||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="product_uom_qty">5</field>
|
||||
<field name="price_unit">500</field>
|
||||
<field name="type">make_to_stock</field>
|
||||
<field name="delay">15</field>
|
||||
</record>
|
||||
|
||||
<!-- Run all schedulers -->
|
||||
<function model="procurement.order" name="run_scheduler"/>
|
||||
|
||||
<!-- sale advance demo.. -->
|
||||
<!-- Demo Data for Product -->
|
||||
|
|
|
@ -22,9 +22,7 @@
|
|||
<form string="Sales Shop" version="7.0">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name"/></h1>
|
||||
<label for="warehouse_id" class="oe_edit_only"/>
|
||||
<h2><field name="warehouse_id" required="1"/></h2>
|
||||
<group>
|
||||
<group name="shop">
|
||||
<group>
|
||||
<field name="payment_default_id"/>
|
||||
<field domain="[('type','=','sale')]" name="pricelist_id" groups="product.group_sale_pricelist"/>
|
||||
|
@ -37,6 +35,7 @@
|
|||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_shop_tree" model="ir.ui.view">
|
||||
<field name="name">sale.shop</field>
|
||||
<field name="model">sale.shop</field>
|
||||
|
@ -44,7 +43,6 @@
|
|||
<field name="arch" type="xml">
|
||||
<tree string="Sales Shop">
|
||||
<field name="name"/>
|
||||
<field name="warehouse_id"/>
|
||||
<field name="pricelist_id" groups="product.group_sale_pricelist"/>
|
||||
<field name="project_id" groups="analytic.group_analytic_accounting"/>
|
||||
</tree>
|
||||
|
@ -61,8 +59,7 @@
|
|||
<field name="help">If you have more than one shop reselling your company products, you can create and manage that from here. Whenever you will record a new quotation or sales order, it has to be linked to a shop. The shop also defines the warehouse from which the products will be delivered for each particular sales.</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_shop_form" id="menu_action_shop_form" parent="base.menu_base_config" sequence="35"
|
||||
groups="stock.group_locations"/>
|
||||
<menuitem action="action_shop_form" id="menu_action_shop_form" parent="base.menu_base_config" sequence="35"/>
|
||||
|
||||
<!-- Sale order Read/Unread actions -->
|
||||
<record id="actions_server_sale_order_unread" model="ir.actions.server">
|
||||
|
@ -136,7 +133,6 @@
|
|||
<field name="date_order"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="user_id"/>
|
||||
<field name="picked_rate" widget="progressbar"/>
|
||||
<field name="invoiced_rate" widget="progressbar"/>
|
||||
<field name="amount_total" sum="Total Tax Included"/>
|
||||
<field name="state"/>
|
||||
|
@ -154,24 +150,17 @@
|
|||
<span groups="base.group_user">
|
||||
<button name="invoice_recreate" states="invoice_except" string="Recreate Invoice"/>
|
||||
<button name="invoice_corrected" states="invoice_except" string="Ignore Exception"/>
|
||||
<button name="ship_recreate" states="shipping_except" string="Recreate Delivery Order"/>
|
||||
<button name="ship_corrected" states="shipping_except" string="Ignore Exception"/>
|
||||
<button name="action_quotation_send" string="Send by Mail" type="object" states="draft,sent"/>
|
||||
<button name="print_quotation" string="Send by Post" type="object" states="draft,sent"/>
|
||||
<button name="action_button_confirm" states="draft" string="Confirm" type="object" class="oe_highlight"/>
|
||||
<button name="action_button_confirm" states="sent" string="Confirm" class="oe_highlight" type="object"/>
|
||||
<button name="action_view_invoice" string="Open Invoice" type="object" class="oe_highlight"
|
||||
attrs="{'invisible': ['|','|',('state', '!=','progress'), ('invoiced', '=', True),('order_policy','=','picking')]}"/>
|
||||
<button name="action_view_delivery" string="Open Delivery Order" type="object" class="oe_highlight"
|
||||
attrs="{'invisible': ['|','|','|',('picking_ids','=',False),('picking_ids','=',[]), ('state', 'not in', ('progress','manual')),('shipped','=',True)]}"/>
|
||||
<button name="%(action_view_sale_advance_payment_inv)d" string="Create Invoice"
|
||||
type="action" states="manual" class="oe_highlight"/>
|
||||
attrs="{'invisible': ['|','|',('state', '!=','progress'), ('invoiced', '=', True)]}"/>
|
||||
<button name="cancel" states="draft,sent" string="Cancel"/>
|
||||
<button name="action_cancel" states="manual,progress" string="Cancel" type="object"/>
|
||||
<button name="ship_cancel" states="shipping_except" string="Cancel"/>
|
||||
<button name="invoice_cancel" states="invoice_except" string="Cancel"/>
|
||||
</span>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,sent,progress,invoiced,done" statusbar_colors='{"shipping_except":"red","invoice_except":"red","waiting_date":"blue"}'/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,sent,progress,invoiced,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
<h1>
|
||||
|
@ -188,7 +177,7 @@
|
|||
</group>
|
||||
<group>
|
||||
<field name="date_order"/>
|
||||
<field name="shop_id" on_change="onchange_shop_id(shop_id)" widget="selection" groups="stock.group_locations"/>
|
||||
<field name="shop_id" on_change="onchange_shop_id(shop_id)" widget="selection"/>
|
||||
<field name="client_order_ref"/>
|
||||
<field domain="[('type','=','sale')]" name="pricelist_id" groups="product.group_sale_pricelist" on_change="onchange_pricelist_id(pricelist_id,order_line)"/>
|
||||
</group>
|
||||
|
@ -207,13 +196,13 @@
|
|||
<field name="product_id"
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}"
|
||||
groups="base.group_user"
|
||||
on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, False, True, parent.date_order, product_packaging, parent.fiscal_position, False, context)"/>
|
||||
on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position, False, context)"/>
|
||||
<label for="product_uom_qty"/>
|
||||
<div>
|
||||
<field
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}"
|
||||
name="product_uom_qty" class="oe_inline"
|
||||
on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, False, False, parent.date_order, product_packaging, parent.fiscal_position, True, context)"/>
|
||||
on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, False, False, parent.date_order, False, parent.fiscal_position, True, context)"/>
|
||||
<field name="product_uom" groups="product.group_uom" class="oe_inline oe_no_button"
|
||||
on_change="product_uom_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, False, False, parent.date_order, context)"/>
|
||||
</div>
|
||||
|
@ -229,22 +218,11 @@
|
|||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<field name="product_packaging"
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}" on_change="product_packaging_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, parent.partner_id, product_packaging, True, context)"
|
||||
domain="[('product_id','=',product_id)]" groups="product.group_stock_packaging"
|
||||
colspan="3"/>
|
||||
<field name="tax_id" widget="many2many_tags" domain="[('parent_id','=',False),('type_tax_use','<>','purchase')]"/>
|
||||
<label for="delay"/>
|
||||
<div>
|
||||
<field name="delay" class="oe_inline"/> days
|
||||
</div>
|
||||
<field name="type"/>
|
||||
<field name="th_weight"/>
|
||||
|
||||
<!-- we should put a config wizard for these two fields -->
|
||||
<field name="address_allotment_id"/>
|
||||
<field name="property_ids" widget="many2many_tags"
|
||||
groups="sale.group_mrp_properties"/>
|
||||
</group>
|
||||
</group>
|
||||
<label for="name"/>
|
||||
|
@ -252,18 +230,13 @@
|
|||
<div groups="base.group_no_one">
|
||||
<label for="invoice_lines"/>
|
||||
<field name="invoice_lines"/>
|
||||
<label for="move_ids"/>
|
||||
<field name="move_ids" widget="many2many"/>
|
||||
</div>
|
||||
</form>
|
||||
<tree string="Sales Order Lines" editable="bottom">
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="sequence" invisible="1"/>
|
||||
<field name="delay" invisible="1"/>
|
||||
<field name="tax_id" invisible="1"/>
|
||||
<field name="type" invisible="1"/>
|
||||
<field name="th_weight" invisible="1"/>
|
||||
<field name="product_packaging" invisible="1"/>
|
||||
<field name="product_id"
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}"
|
||||
groups="base.group_user"
|
||||
|
@ -297,9 +270,7 @@
|
|||
<page string="Other Information" groups="base.group_user">
|
||||
<group>
|
||||
<group name="logistics">
|
||||
<field name="incoterm" widget="selection" groups="base.group_user"/>
|
||||
<field name="picking_policy" required="True"/>
|
||||
<field name="order_policy" on_change="shipping_policy_change(order_policy)"/>
|
||||
<field name="order_policy"/>
|
||||
<field name="invoice_quantity" invisible="True"/>
|
||||
</group>
|
||||
<group groups="base.group_user">
|
||||
|
@ -313,7 +284,6 @@
|
|||
</group>
|
||||
<group>
|
||||
<field name="invoiced"/>
|
||||
<field name="shipped"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
|
@ -330,16 +300,6 @@
|
|||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
<separator string="Delivery Orders"/>
|
||||
<field name="picking_ids" context="{'default_type': 'out'}">
|
||||
<tree colors="blue:state == 'draft';grey:state == 'cancel';red:state not in ('cancel', 'done') and min_date < current_date" string="Delivery Orders">
|
||||
<field name="name"/>
|
||||
<field name="partner_id" string="Customer"/>
|
||||
<field name="min_date"/>
|
||||
<field name="state"/>
|
||||
<button name="action_process" states="assigned" string="Deliver" type="object" icon="gtk-go-forward" context="{'default_type': 'out'}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
|
|
|
@ -30,10 +30,6 @@
|
|||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<field name="name">wait_invoice</field>
|
||||
</record>
|
||||
<record id="act_wait_ship" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<field name="name">wait_ship</field>
|
||||
</record>
|
||||
|
||||
<record id="act_done" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
|
@ -83,6 +79,7 @@
|
|||
<field name="kind">function</field>
|
||||
<field name="action">action_invoice_end()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_invoice_cancel" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<field name="name">invoice_cancel</field>
|
||||
|
@ -90,31 +87,6 @@
|
|||
<field name="kind">stopall</field>
|
||||
<field name="action">action_cancel()</field>
|
||||
</record>
|
||||
<record id="act_ship" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<field name="name">ship</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">action_ship_create()</field>
|
||||
</record>
|
||||
<record id="act_ship_except" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<field name="name">ship_except</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'state':'shipping_except'})</field>
|
||||
</record>
|
||||
<record id="act_ship_end" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<field name="name">ship_end</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">action_ship_end()</field>
|
||||
</record>
|
||||
<record id="act_ship_cancel" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<field name="name">ship_cancel</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="kind">stopall</field>
|
||||
<field name="action">action_cancel()</field>
|
||||
</record>
|
||||
|
||||
<!-- Transistion -->
|
||||
|
||||
|
@ -159,6 +131,7 @@
|
|||
<field name="signal">all_lines</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="trans_router_wait_invoice_shipping" model="workflow.transition">
|
||||
<field name="act_from" ref="act_wait_invoice"/>
|
||||
<field name="act_to" ref="act_invoice_end"/>
|
||||
|
@ -170,10 +143,6 @@
|
|||
<field name="act_to" ref="act_wait_invoice"/>
|
||||
</record>
|
||||
|
||||
<record id="trans_router_wait_ship" model="workflow.transition">
|
||||
<field name="act_from" ref="act_router"/>
|
||||
<field name="act_to" ref="act_wait_ship"/>
|
||||
</record>
|
||||
|
||||
<record id="trans_wait_invoice_cancel2" model="workflow.transition">
|
||||
<field name="act_from" ref="act_wait_invoice"/>
|
||||
|
@ -181,17 +150,6 @@
|
|||
<field name="signal">cancel</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_wait_ship_cancel3" model="workflow.transition">
|
||||
<field name="act_from" ref="act_wait_ship"/>
|
||||
<field name="act_to" ref="act_cancel3"/>
|
||||
<field name="signal">cancel</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_wait_ship_ship" model="workflow.transition">
|
||||
<field name="act_from" ref="act_wait_ship"/>
|
||||
<field name="act_to" ref="act_ship"/>
|
||||
<field name="condition">(order_policy!='prepaid') or invoiced</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_wait_invoice_invoice" model="workflow.transition">
|
||||
<field name="act_from" ref="act_wait_invoice"/>
|
||||
|
@ -199,17 +157,13 @@
|
|||
<field name="condition">(order_policy=='prepaid')</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_wait_invoice_invoice_manual" model="workflow.transition">
|
||||
<field name="act_from" ref="act_wait_invoice"/>
|
||||
<field name="act_to" ref="act_invoice"/>
|
||||
<field name="signal">manual_invoice</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_invoice_invoice_end" model="workflow.transition">
|
||||
<field name="act_from" ref="act_invoice"/>
|
||||
<field name="act_to" ref="act_invoice_end"/>
|
||||
<field name="signal">subflow.paid</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_invoice_invoice_except" model="workflow.transition">
|
||||
<field name="act_from" ref="act_invoice"/>
|
||||
<field name="act_to" ref="act_invoice_except"/>
|
||||
|
@ -230,38 +184,6 @@
|
|||
<field name="act_to" ref="act_invoice_cancel"/>
|
||||
<field name="signal">invoice_cancel</field>
|
||||
</record>
|
||||
<record id="trans_ship_end_done" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship_end"/>
|
||||
<field name="act_to" ref="act_done"/>
|
||||
</record>
|
||||
|
||||
<record id="trans_ship_ship_end" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship"/>
|
||||
<field name="act_to" ref="act_ship_end"/>
|
||||
<field name="trigger_model">procurement.order</field>
|
||||
<field name="trigger_expr_id">procurement_lines_get()</field>
|
||||
<field name="condition">test_state('finished')</field>
|
||||
</record>
|
||||
<record id="trans_ship_ship_except" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship"/>
|
||||
<field name="act_to" ref="act_ship_except"/>
|
||||
<field name="condition">test_state('canceled')</field>
|
||||
</record>
|
||||
<record id="trans_ship_except_ship" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship_except"/>
|
||||
<field name="act_to" ref="act_ship"/>
|
||||
<field name="signal">ship_recreate</field>
|
||||
</record>
|
||||
<record id="trans_ship_except_ship_end" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship_except"/>
|
||||
<field name="act_to" ref="act_ship_end"/>
|
||||
<field name="signal">ship_corrected</field>
|
||||
</record>
|
||||
<record id="trans_ship_except_ship_cancel" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship_except"/>
|
||||
<field name="act_to" ref="act_ship_cancel"/>
|
||||
<field name="signal">ship_cancel</field>
|
||||
</record>
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -9,13 +9,6 @@ access_account_invoice_manager,account_invoice manager,account.model_account_inv
|
|||
access_account_invoice_line_salesman,account_invoice.line salesman,account.model_account_invoice_line,base.group_sale_salesman,1,1,1,0
|
||||
access_account_payment_term_salesman,account_payment_term salesman,account.model_account_payment_term,base.group_sale_salesman,1,0,0,0
|
||||
access_account_analytic_account_salesman,account_analytic_account salesman,analytic.model_account_analytic_account,base.group_sale_salesman,1,1,1,0
|
||||
access_stock_picking_salesman,stock_picking salesman,stock.model_stock_picking,base.group_sale_salesman,1,1,1,0
|
||||
access_stock_move_salesman,stock_move salesman,stock.model_stock_move,base.group_sale_salesman,1,1,1,0
|
||||
access_stock_move_manager,stock_move manager,stock.model_stock_move,base.group_sale_manager,1,1,1,1
|
||||
access_procurement_salesman,procurement.order salesman,procurement.model_procurement_order,base.group_sale_salesman,1,1,1,0
|
||||
access_procurement_manager,procurement.order manager,procurement.model_procurement_order,base.group_sale_manager,1,1,1,1
|
||||
access_sale_order_stock_worker,sale.order stock worker,model_sale_order,stock.group_stock_user,1,1,0,0
|
||||
access_sale_order_line_stock_worker,sale.order.line stock worker,model_sale_order_line,stock.group_stock_user,1,1,0,0
|
||||
access_sale_order_manager,sale.order.manager,model_sale_order,base.group_sale_manager,1,1,1,1
|
||||
access_sale_order_accountant,sale.order.accountant,model_sale_order,account.group_account_user,1,1,0,0
|
||||
access_sale_report_salesman,sale.report,model_sale_report,base.group_sale_salesman,1,1,1,0
|
||||
|
@ -24,23 +17,19 @@ access_ir_property_sales,ir.property.sales,base.model_ir_property,base.group_sal
|
|||
access_sale_account_journal,account.journal sale order.user,account.model_account_journal,base.group_sale_salesman,1,0,0,0
|
||||
access_res_partner_sale_user,res.partner.sale.user,base.model_res_partner,base.group_sale_salesman,1,0,0,0
|
||||
access_res_partner_sale_manager,res.partner.sale.manager,base.model_res_partner,base.group_sale_manager,1,1,1,0
|
||||
access_report_stock_move_sales,report.stock.move.sale,stock.model_report_stock_move,base.group_sale_manager,1,1,1,1
|
||||
access_stock_picking_sales,stock.picking.sales,stock.model_stock_picking,base.group_sale_manager,1,1,1,1
|
||||
|
||||
access_product_template_sale_user,product.template sale use,product.model_product_template,base.group_sale_salesman,1,0,0,0
|
||||
access_product_product_sale_user,product.product sale use,product.model_product_product,base.group_sale_salesman,1,0,0,0
|
||||
access_sale_shop_manager,account.journal sale order.user,model_sale_shop,base.group_sale_manager,1,1,1,1
|
||||
access_account_fiscalyear_user,account.fiscalyear.user,account.model_account_fiscalyear,base.group_sale_salesman,1,0,0,0
|
||||
access_account_tax_user,account.tax.user,account.model_account_tax,base.group_sale_salesman,1,0,0,0
|
||||
access_product_packaging_user,product.packaging.user,product.model_product_packaging,base.group_sale_salesman,1,1,1,0
|
||||
|
||||
access_ir_attachment_sales,ir.attachment.sales,base.model_ir_attachment,base.group_sale_salesman,1,1,1,0
|
||||
access_ir_attachment_manager,ir.attachment.manager,base.model_ir_attachment,base.group_sale_manager,1,1,1,1
|
||||
access_product_packaging_manager,product.packaging.manager,product.model_product_packaging,base.group_sale_manager,1,0,0,0
|
||||
access_stock_warehouse_user,stock.warehouse.user,stock.model_stock_warehouse,base.group_sale_salesman,1,0,0,0
|
||||
|
||||
access_res_partner_bank_type_user,res.partner.bank.type.user,base.model_res_partner_bank_type,base.group_sale_salesman,1,0,0,0
|
||||
access_stock_location_user,stock.location.user,stock.model_stock_location,base.group_sale_salesman,1,0,0,0
|
||||
access_res_partner_bank_type_field_user,res.partner.bank.type.field.user,base.model_res_partner_bank_type_field,base.group_sale_salesman,1,0,0,0
|
||||
access_product_uom_user,product.uom.user,product.model_product_uom,base.group_sale_salesman,1,0,0,0
|
||||
access_product_supplierinfo_user,product.supplierinfo.user,product.model_product_supplierinfo,base.group_sale_salesman,1,0,0,0
|
||||
access_product_pricelist_sale_user,product.pricelist.sale.user,product.model_product_pricelist,base.group_sale_salesman,1,0,0,0
|
||||
access_account_account_salesman,account_account salesman,account.model_account_account,base.group_sale_salesman,1,0,0,0
|
||||
access_sale_shop_sale_user,sale.shop.sale.user,model_sale_shop,base.group_sale_salesman,1,0,0,0
|
||||
|
@ -49,7 +38,7 @@ access_product_uom_categ_sale_manager,product.uom.categ salemanager,product.mode
|
|||
access_product_uom_sale_manager,product.uom salemanager,product.model_product_uom,base.group_sale_manager,1,1,1,1
|
||||
access_product_ul_sale_manager,product.ul salemanager,product.model_product_ul,base.group_sale_manager,1,1,1,1
|
||||
access_product_category_sale_manager,product.category salemanager,product.model_product_category,base.group_sale_manager,1,1,1,1
|
||||
access_product_packaging_sale_manager,product.packaging salemanager,product.model_product_packaging,base.group_sale_manager,1,1,1,1
|
||||
access_product_supplierinfo_user,product.supplierinfo.user,product.model_product_supplierinfo,base.group_sale_salesman,1,0,0,0
|
||||
access_product_supplierinfo_sale_manager,product.supplierinfo salemanager,product.model_product_supplierinfo,base.group_sale_manager,1,1,1,1
|
||||
access_pricelist_partnerinfo_sale_manager,pricelist.partnerinfo salemanager,product.model_pricelist_partnerinfo,base.group_sale_manager,1,1,1,1
|
||||
access_product_price_type_sale_manager,product.price.type salemanager,product.model_product_price_type,base.group_sale_manager,1,1,1,1
|
||||
|
@ -58,5 +47,3 @@ access_product_pricelist_sale_manager,product.pricelist salemanager,product.mode
|
|||
access_product_group_res_partner_sale_manager,res_partner group_sale_manager,base.model_res_partner,base.group_sale_manager,1,1,1,0
|
||||
access_product_pricelist_version_sale_manager,product.pricelist.version sale_manager,product.model_product_pricelist_version,base.group_sale_manager,1,1,1,1
|
||||
access_account_invoice_report_salesman,account.invoice.report salesman,account.model_account_invoice_report,base.group_sale_salesman,1,0,0,0
|
||||
access_stock_warehouse_orderpoint_sale_salesman,stock.warehouse.orderpoint,procurement.model_stock_warehouse_orderpoint,base.group_sale_salesman,1,0,0,0
|
||||
access_account_move_line_salesman,account.move.line invoice,account.model_account_move_line,base.group_sale_salesman,1,0,0,0
|
||||
|
|
|
|
@ -25,21 +25,12 @@
|
|||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="group_invoice_deli_orders" model="res.groups">
|
||||
<field name="name">Enable Invoicing Delivery orders</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="group_delivery_invoice_address" model="res.groups">
|
||||
<field name="name">Addresses in Sale Orders</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="group_mrp_properties" model="res.groups">
|
||||
<field name="name">Properties on lines</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="group_discount_per_so_line" model="res.groups">
|
||||
<field name="name">Discount on lines</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
|
|
|
@ -1,67 +1,20 @@
|
|||
-
|
||||
In order to test the cancel sale order.
|
||||
-
|
||||
Now I cancel Quotation.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
self.action_cancel(cr, uid, [ref("order7")])
|
||||
-
|
||||
I check state of Quotation after cancelled.
|
||||
-
|
||||
!assert {model: sale.order, id: order7, string: sale order should be in cancel state}:
|
||||
- state == 'cancel'
|
||||
-
|
||||
I set cancelled quotation to draft.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
self.action_cancel_draft(cr, uid, [ref("order7")])
|
||||
-
|
||||
I confirm order.
|
||||
|
||||
First I confirm order.
|
||||
-
|
||||
!workflow {model: sale.order, action: order_confirm, ref: order7}
|
||||
-
|
||||
I send delivery in two shipments, so I am doing a partial delivery order.
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
delivery_orders = self.search(cr, uid, [('sale_id','=',ref("order7"))])
|
||||
first_picking = self.browse(cr, uid, delivery_orders[0], context=context)
|
||||
if first_picking.force_assign(cr, uid, first_picking):
|
||||
first_move = first_picking.move_lines[0]
|
||||
values = {'move%s'%(first_move.id): {'product_qty': 5, 'product_uom':ref('product.product_uom_unit')}}
|
||||
first_picking.do_partial(values, context=context)
|
||||
-
|
||||
Now I cancel latest shipment.
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
import netsvc
|
||||
delivery_orders = self.search(cr, uid, [('sale_id','=',ref("order7"))])
|
||||
last_delivery_order_id = delivery_orders[0]
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
wf_service.trg_validate(uid, 'stock.picking', last_delivery_order_id, 'button_cancel', cr)
|
||||
-
|
||||
I run the scheduler.
|
||||
-
|
||||
!python {model: procurement.order}: |
|
||||
|
||||
self.run_scheduler(cr, uid)
|
||||
-
|
||||
I check order status in "Ship Exception".
|
||||
-
|
||||
!assert {model: sale.order, id: order7, string: Sale order should be in shipping exception}:
|
||||
- state == "shipping_except"
|
||||
-
|
||||
Now I regenerate shipment.
|
||||
-
|
||||
!workflow {model: sale.order, action: ship_recreate, ref: order7}
|
||||
-
|
||||
I check state of order in 'To Invoice'.
|
||||
I check state of order in 'Sale Order'.
|
||||
-
|
||||
!assert {model: sale.order, id: order7, string: Sale order should be In Progress state}:
|
||||
- state == 'manual'
|
||||
- state == 'progress'
|
||||
-
|
||||
I check that Invoice should be created.
|
||||
-
|
||||
I make invoice for order.
|
||||
-
|
||||
!workflow {model: sale.order, action: manual_invoice, ref: order7}
|
||||
!python {model: sale.order}: |
|
||||
sale_order = self.browse(cr, uid, ref("order7"))
|
||||
assert len(sale_order.invoice_ids) == True, "Invoice should be created."
|
||||
-
|
||||
To cancel the sale order from Invoice Exception, I have to cancel the invoice of sale order.
|
||||
-
|
||||
|
@ -80,9 +33,3 @@
|
|||
Then I click on the Ignore Exception button.
|
||||
-
|
||||
!workflow {model: sale.order, action: invoice_corrected, ref: order7}
|
||||
|
||||
-
|
||||
I check state of order in 'In Progress'.
|
||||
-
|
||||
!assert {model: sale.order, id: order7, string: Sale order should be In progress state}:
|
||||
- state == 'progress'
|
||||
|
|
|
@ -10,5 +10,5 @@
|
|||
I make duplicate order and delete.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
id = self.copy(cr, uid, ref('order'))
|
||||
id = self.copy(cr, uid, ref('order4'))
|
||||
self.unlink(cr, uid, [id])
|
||||
|
|
|
@ -108,12 +108,12 @@
|
|||
assert len(order_new.order_line) == 2, "Sale order lines mismatch"
|
||||
for sale_line in order_new.order_line:
|
||||
if sale_line.name == 'Basic PC':
|
||||
assert sale_line.delay == 18 , "incorrect delay: got %s, expected 18"%(sale_line.delay,)
|
||||
# assert sale_line.delay == 18 , "incorrect delay: got %s, expected 18"%(sale_line.delay,)
|
||||
assert sale_line.product_uom.name == "Unit" , "uom is not same"
|
||||
assert sale_line.price_unit == 150 , "unit price is not same, got %s, expected 150"%(sale_line.price_unit,)
|
||||
assert sale_line.product_uom_qty == 1 , "product qty is not same"
|
||||
elif sale_line.name == 'Medium PC':
|
||||
assert sale_line.delay == 3 , "incorrect delay: got %s, expected 3"%(sale_line.delay,)
|
||||
# assert sale_line.delay == 3 , "incorrect delay: got %s, expected 3"%(sale_line.delay,)
|
||||
assert sale_line.product_uom.name == "Unit" , "uom is not same"
|
||||
assert sale_line.price_unit == 100 , "unit price is not same, got %s, expected 100"%(sale_line.price_unit,)
|
||||
assert sale_line.product_uom_qty == 2 , "product qty is not same"
|
||||
|
|
|
@ -7,5 +7,4 @@
|
|||
-
|
||||
!python {model: sale.order}: |
|
||||
sale_order = self.browse(cr, uid, ref("order3"))
|
||||
assert len(sale_order.picking_ids) == False, "Delivery order should not created before invoice."
|
||||
assert sale_order.invoice_ids, "Invoice should be created."
|
||||
|
|
|
@ -18,9 +18,6 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import sale_make_invoice
|
||||
import sale_line_invoice
|
||||
import sale_make_invoice_advance
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -37,7 +37,7 @@ modules.
|
|||
'author': 'OpenERP SA',
|
||||
'website': 'http://www.openerp.com',
|
||||
'images': ['images/crm_statistics_dashboard.jpeg', 'images/opportunity_to_quote.jpeg'],
|
||||
'depends': ['sale', 'crm'],
|
||||
'depends': ['sale_stock', 'crm'],
|
||||
'init_xml': [],
|
||||
'update_xml': ['wizard/crm_make_sale_view.xml',
|
||||
'sale_crm_view.xml',
|
||||
|
|
|
@ -49,7 +49,7 @@ Some statistics by journals are provided.
|
|||
'author': 'OpenERP SA',
|
||||
'website': 'http://www.openerp.com',
|
||||
'images': ['images/invoice_type.jpeg'],
|
||||
'depends': ['sale'],
|
||||
'depends': ['sale_stock'],
|
||||
'init_xml': ['sale_journal_data.xml'],
|
||||
'update_xml': [
|
||||
'security/ir.model.access.csv',
|
||||
|
|
|
@ -31,7 +31,7 @@ Price and Cost Price.
|
|||
""",
|
||||
"author":"OpenERP SA",
|
||||
"images":["images/sale_margin.jpeg"],
|
||||
"depends":["sale"],
|
||||
"depends":["sale_stock"],
|
||||
"demo_xml":[],
|
||||
'test': ['test/sale_margin.yml'],
|
||||
"update_xml":["security/ir.model.access.csv","sale_margin_view.xml"],
|
||||
|
|
|
@ -34,7 +34,7 @@ from sales order. It adds sales name and sales Reference on production order.
|
|||
'author': 'OpenERP SA',
|
||||
'website': 'http://www.openerp.com',
|
||||
'images': ['images/SO_to_MO.jpeg'],
|
||||
'depends': ['mrp', 'sale'],
|
||||
'depends': ['mrp', 'sale_stock'],
|
||||
'init_xml': [],
|
||||
'update_xml': [
|
||||
'security/ir.model.access.csv',
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import sale_stock
|
||||
import stock
|
||||
import wizard
|
||||
import report
|
||||
import company
|
||||
import res_config
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,87 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
{
|
||||
'name': 'Sale To Stock',
|
||||
'version': '1.0',
|
||||
'category': 'Hidden',
|
||||
'description': """
|
||||
This Module manage quotations and sales orders.
|
||||
===========================================================================
|
||||
|
||||
Workflow with validation steps:
|
||||
-------------------------------
|
||||
* Quotation -> Sales order -> Invoice
|
||||
|
||||
Create Invoice:
|
||||
---------------
|
||||
* Invoice on Demand
|
||||
* Invoice on Delivery Order
|
||||
* Invoice Before Delivery
|
||||
|
||||
Partners preferences:
|
||||
---------------------
|
||||
* Incoterm
|
||||
* Shipping
|
||||
* Invoicing
|
||||
|
||||
Products stocks and prices:
|
||||
--------------------------
|
||||
|
||||
Delivery method:
|
||||
-----------------
|
||||
* The Poste
|
||||
* Free Delivery Charges
|
||||
* Normal Delivery Charges
|
||||
* Based on the Delivery Order(if not Add to sale order)
|
||||
|
||||
Dashboard for Sales Manager that includes:
|
||||
------------------------------------------
|
||||
* My Quotations
|
||||
* Monthly Turnover (Graph)
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'website': 'http://www.openerp.com',
|
||||
'images': [],
|
||||
'depends': ['sale', 'procurement'],
|
||||
'init_xml': [],
|
||||
'update_xml': ['wizard/sale_make_invoice_advance.xml',
|
||||
'wizard/sale_make_invoice.xml',
|
||||
'security/sale_stock_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'company_view.xml',
|
||||
'sale_stock_view.xml',
|
||||
'sale_stock_workflow.xml',
|
||||
'res_config_view.xml',
|
||||
'report/sale_report_view.xml',
|
||||
'process/sale_stock_process.xml',
|
||||
],
|
||||
'data': ['sale_stock_data.xml'],
|
||||
'demo_xml': ['sale_stock_demo.xml'],
|
||||
'test': ['test/cancel_order_sale_stock.yml',
|
||||
'test/picking_order_policy.yml',
|
||||
'test/manual_order_policy.yml'
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,142 @@
|
|||
<?xml version="1.0" ?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!--
|
||||
Process Node
|
||||
-->
|
||||
|
||||
<record id="process_node_saleorderprocurement0" model="process.node">
|
||||
<field name="menu_id" ref="sale.menu_sale_order"/>
|
||||
<field name="model_id" ref="sale.model_sale_order"/>
|
||||
<field eval=""""subflow"""" name="kind"/>
|
||||
<field eval=""""Sales Order Requisition"""" name="name"/>
|
||||
<field eval=""""Drives procurement orders for every sales order line."""" name="note"/>
|
||||
<field name="subflow_id" ref="sale.process_process_salesprocess0"/>
|
||||
<field name="process_id" ref="procurement.process_process_procurementprocess0"/>
|
||||
<field eval=""""object.state in ('draft', 'waiting_date', 'manual', 'progress', 'shipping_except', 'invoice_except', 'done', 'cancel')"""" name="model_states"/>
|
||||
<field eval="1" name="flow_start"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="process_node_saleprocurement0" model="process.node">
|
||||
<field name="menu_id" ref="procurement.menu_stock_procurement_action"/>
|
||||
<field name="model_id" ref="procurement.model_procurement_order"/>
|
||||
<field eval=""""subflow"""" name="kind"/>
|
||||
<field eval=""""Procurement Order"""" name="name"/>
|
||||
<field eval=""""One Procurement order for each sales order line and for each of the components."""" name="note"/>
|
||||
<field name="subflow_id" ref="procurement.process_process_procurementprocess0"/>
|
||||
<field name="process_id" ref="sale.process_process_salesprocess0"/>
|
||||
<field eval=""""object.state=='confirmed'"""" name="model_states"/>
|
||||
<field eval="1" name="flow_start"/>
|
||||
</record>
|
||||
|
||||
<record id="process_node_packinglist0" model="process.node">
|
||||
<field name="model_id" ref="stock.model_stock_picking"/>
|
||||
<field eval=""""state"""" name="kind"/>
|
||||
<field eval=""""Pick List"""" name="name"/>
|
||||
<field eval=""""Document of the move to the output or to the customer."""" name="note"/>
|
||||
<field name="process_id" ref="sale.process_process_salesprocess0"/>
|
||||
<field eval=""""object.state in ('assigned', 'confirmed')"""" name="model_states"/>
|
||||
<field eval="0" name="flow_start"/>
|
||||
</record>
|
||||
|
||||
<record id="process_node_deliveryorder0" model="process.node">
|
||||
<field name="model_id" ref="stock.model_stock_picking"/>
|
||||
<field eval=""""state"""" name="kind"/>
|
||||
<field eval=""""Delivery Order"""" name="name"/>
|
||||
<field eval=""""Document of the move to the customer."""" name="note"/>
|
||||
<field name="process_id" ref="sale.process_process_salesprocess0"/>
|
||||
<field eval=""""object.state in ('done', 'assigned')"""" name="model_states"/>
|
||||
<field eval="0" name="flow_start"/>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
Process Transition
|
||||
-->
|
||||
|
||||
|
||||
<record id="process_transition_saleprocurement0" model="process.transition">
|
||||
<field eval="[(6,0,[])]" name="transition_ids"/>
|
||||
<field eval=""""Create Procurement Order"""" name="name"/>
|
||||
<field eval=""""A procurement order is automatically created as soon as a sales order is confirmed or as the invoice is paid. It drives the purchasing and the production of products regarding to the rules and to the sales order's parameters. """" name="note"/>
|
||||
<field model="process.node" name="target_node_id" ref="process_node_saleprocurement0"/>
|
||||
<field model="process.node" name="source_node_id" ref="sale.process_node_saleorder0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_packing0" model="process.transition">
|
||||
<field eval="[(6,0,[])]" name="transition_ids"/>
|
||||
<field eval=""""Create Pick List"""" name="name"/>
|
||||
<field eval=""""The Pick List form is created as soon as the sales order is confirmed, in the same time as the procurement order. It represents the assignment of parts to the sales order. There is 1 pick list by sales order line which evolves with the availability of parts."""" name="note"/>
|
||||
<field model="process.node" name="target_node_id" ref="process_node_packinglist0"/>
|
||||
<field model="process.node" name="source_node_id" ref="process_node_saleprocurement0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_deliver0" model="process.transition">
|
||||
<field eval="[(6,0,[])]" name="transition_ids"/>
|
||||
<field eval=""""Create Delivery Order"""" name="name"/>
|
||||
<field eval=""""Depending on the configuration of the location Output, the move between the output area and the customer is done through the Delivery Order manually or automatically."""" name="note"/>
|
||||
<field model="process.node" name="target_node_id" ref="process_node_deliveryorder0"/>
|
||||
<field model="process.node" name="source_node_id" ref="process_node_packinglist0"/>
|
||||
</record>
|
||||
<record id="process_transition_action_assign0" model="process.transition.action">
|
||||
<field eval=""""action_assign"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Assign"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_packing0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_forceassignation0" model="process.transition.action">
|
||||
<field eval=""""force_assign"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Force Assignation"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_packing0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_cancel1" model="process.transition.action">
|
||||
<field eval=""""action_cancel"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Cancel"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_packing0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_cancelassignation0" model="process.transition.action">
|
||||
<field eval=""""cancel_assign"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Cancel Assignation"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_deliver0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_validate0" model="process.transition.action">
|
||||
<field eval=""""test_finished"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Validate"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_deliver0"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_invoice0" model="process.transition">
|
||||
<field eval="[(6,0,[])]" name="transition_ids"/>
|
||||
<field eval=""""Create Invoice"""" name="name"/>
|
||||
<field eval=""""The Salesman creates an invoice manually, if the sales order shipping policy is 'Shipping and Manual in Progress'. The invoice is created automatically if the shipping policy is 'Payment before Delivery'."""" name="note"/>
|
||||
<field model="process.node" name="target_node_id" ref="sale.process_node_invoice0"/>
|
||||
<field model="process.node" name="source_node_id" ref="sale.process_node_saleorder0"/>
|
||||
<field eval="[(6,0,[ref('sale_stock.trans_wait_invoice_invoice_manual')])]" name="transition_ids"/>
|
||||
</record>
|
||||
|
||||
<record id="process_transition_action_cancelorder0" model="process.transition.action">
|
||||
<field eval=""""action_cancel"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Cancel Order"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_invoice0"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="process_transition_action_cancel2" model="process.transition.action">
|
||||
<field eval=""""action_cancel"""" name="action"/>
|
||||
<field eval=""""object"""" name="state"/>
|
||||
<field eval=""""Cancel"""" name="name"/>
|
||||
<field name="transition_id" ref="process_transition_deliver0"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import sale_report
|
|
@ -0,0 +1,93 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields, osv
|
||||
import tools
|
||||
class sale_report(osv.osv):
|
||||
_inherit = "sale.report"
|
||||
_columns = {
|
||||
'shipped': fields.boolean('Shipped', readonly=True),
|
||||
'shipped_qty_1': fields.integer('Shipped', readonly=True),
|
||||
'state': fields.selection([
|
||||
('draft', 'Quotation'),
|
||||
('waiting_date', 'Waiting Schedule'),
|
||||
('manual', 'Manual In Progress'),
|
||||
('progress', 'In Progress'),
|
||||
('shipping_except', 'Shipping Exception'),
|
||||
('invoice_except', 'Invoice Exception'),
|
||||
('done', 'Done'),
|
||||
('cancel', 'Cancelled')
|
||||
], 'Order State', readonly=True),
|
||||
}
|
||||
|
||||
def init(self, cr):
|
||||
tools.drop_view_if_exists(cr, 'sale_report')
|
||||
cr.execute("""
|
||||
create or replace view sale_report as (
|
||||
select
|
||||
min(l.id) as id,
|
||||
l.product_id as product_id,
|
||||
t.uom_id as product_uom,
|
||||
sum(l.product_uom_qty / u.factor * u2.factor) as product_uom_qty,
|
||||
sum(l.product_uom_qty * l.price_unit * (100.0-l.discount) / 100.0) as price_total,
|
||||
1 as nbr,
|
||||
s.date_order as date,
|
||||
s.date_confirm as date_confirm,
|
||||
to_char(s.date_order, 'YYYY') as year,
|
||||
to_char(s.date_order, 'MM') as month,
|
||||
to_char(s.date_order, 'YYYY-MM-DD') as day,
|
||||
s.partner_id as partner_id,
|
||||
s.user_id as user_id,
|
||||
s.shop_id as shop_id,
|
||||
s.company_id as company_id,
|
||||
extract(epoch from avg(date_trunc('day',s.date_confirm)-date_trunc('day',s.create_date)))/(24*60*60)::decimal(16,2) as delay,
|
||||
s.state,
|
||||
t.categ_id as categ_id,
|
||||
s.shipped,
|
||||
s.shipped::integer as shipped_qty_1,
|
||||
s.pricelist_id as pricelist_id,
|
||||
s.project_id as analytic_account_id
|
||||
from
|
||||
sale_order s
|
||||
left join sale_order_line l on (s.id=l.order_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_uom u on (u.id=l.product_uom)
|
||||
left join product_uom u2 on (u2.id=t.uom_id)
|
||||
group by
|
||||
l.product_id,
|
||||
l.product_uom_qty,
|
||||
l.order_id,
|
||||
t.uom_id,
|
||||
t.categ_id,
|
||||
s.date_order,
|
||||
s.date_confirm,
|
||||
s.partner_id,
|
||||
s.user_id,
|
||||
s.shop_id,
|
||||
s.company_id,
|
||||
s.state,
|
||||
s.shipped,
|
||||
s.pricelist_id,
|
||||
s.project_id
|
||||
)
|
||||
""")
|
||||
sale_report()
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="view_order_product_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">sale.report.tree.sale.stock</field>
|
||||
<field name="model">sale.report</field>
|
||||
<field name="inherit_id" ref="sale.view_order_product_tree"/>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="product_uom_qty" position="after">
|
||||
<field name="shipped_qty_1" sum="Shipped"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,86 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Business Applications
|
||||
# Copyright (C) 2004-2012 OpenERP S.A. (<http://openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields, osv
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
|
||||
class sale_configuration(osv.osv_memory):
|
||||
_inherit = 'sale.config.settings'
|
||||
_columns = {
|
||||
'group_invoice_deli_orders': fields.boolean('Based on Delivery Orders',
|
||||
implied_group='group_invoice_deli_orders',
|
||||
help="To allow your salesman to make invoices for Delivery Orders using the menu 'Deliveries to Invoice'."),
|
||||
'task_work': fields.boolean('Based on Task Activities',
|
||||
help="""Lets you transfer the entries under tasks defined for Project Management to
|
||||
the Timesheet line entries for particular date and particular user with the effect of creating, editing and deleting either ways
|
||||
and to automatically creates project tasks from procurement lines.
|
||||
This installs the modules project_timesheet and project_mrp."""),
|
||||
'default_order_policy': fields.selection(
|
||||
[('manual', 'Invoice Based on Sales Orders'), ('picking', 'Invoice Based on Deliveries')],
|
||||
'Default Method', default_model='sale.order',
|
||||
help="You can generate invoices based on sales orders or based on shippings."),
|
||||
'module_delivery': fields.boolean('Allow Charging Shipping Costs',
|
||||
help ="""Allows you to add delivery methods in sale orders and delivery orders.
|
||||
You can define your own carrier and delivery grids for prices.
|
||||
This installs the module delivery."""),
|
||||
'default_picking_policy' : fields.boolean("Configurable Shipping Policy",
|
||||
help = "You will be able to configure, per sale order, if you deliver all products at once or if you deliver each product when it is available. This may have an impact on the shipping price."),
|
||||
'group_mrp_properties': fields.boolean('Properties on Lines',
|
||||
implied_group='sale.group_mrp_properties',
|
||||
help="Allows you to tag sale order lines with properties."),
|
||||
'group_multiple_shops': fields.boolean("Manage Multiple Shops",
|
||||
implied_group='stock.group_locations',
|
||||
help="This allows to configure and use multiple shops."),
|
||||
'module_project_timesheet': fields.boolean("Project Timesheet"),
|
||||
'module_project_mrp': fields.boolean("Project MRP"),
|
||||
|
||||
}
|
||||
|
||||
def default_get(self, cr, uid, fields, context=None):
|
||||
res = super(sale_configuration, self).default_get(cr, uid, fields, context)
|
||||
# task_work, time_unit depend on other fields
|
||||
res['task_work'] = res.get('module_project_mrp') and res.get('module_project_timesheet')
|
||||
return res
|
||||
|
||||
def get_default_sale_config(self, cr, uid, ids, context=None):
|
||||
ir_values = self.pool.get('ir.values')
|
||||
default_picking_policy = ir_values.get_default(cr, uid, 'sale.order', 'picking_policy')
|
||||
return {
|
||||
'default_picking_policy': default_picking_policy == 'one',
|
||||
}
|
||||
|
||||
def set_sale_defaults(self, cr, uid, ids, context=None):
|
||||
ir_values = self.pool.get('ir.values')
|
||||
ir_model_data = self.pool.get('ir.model.data')
|
||||
wizard = self.browse(cr, uid, ids)[0]
|
||||
|
||||
default_picking_policy = 'one' if wizard.default_picking_policy else 'direct'
|
||||
ir_values.set_default(cr, uid, 'sale.order', 'picking_policy', default_picking_policy)
|
||||
res = super(sale_configuration, self).set_sale_defaults(cr, uid, ids, context)
|
||||
return res
|
||||
|
||||
def onchange_invoice_methods(self, cr, uid, ids, group_invoice_so_lines, group_invoice_deli_orders, context=None):
|
||||
if not group_invoice_deli_orders:
|
||||
return {'value': {'default_order_policy': 'manual'}}
|
||||
if not group_invoice_so_lines:
|
||||
return {'value': {'default_order_policy': 'picking'}}
|
||||
return {}
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="view_sales_config_sale_stock" model="ir.ui.view">
|
||||
<field name="name">sale settings</field>
|
||||
<field name="model">sale.config.settings</field>
|
||||
<field name="type">form</field>
|
||||
<field name="inherit_id" ref="sale.view_sales_config"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<group name="config_sale" position="inside">
|
||||
<field name="module_project_timesheet" invisible="1"/>
|
||||
<field name="module_project_mrp" invisible="1"/>
|
||||
</group>
|
||||
<field name="group_sale_pricelist" position="after">
|
||||
<field name="group_multiple_shops"/>
|
||||
</field>
|
||||
<field name="decimal_precision" position="after">
|
||||
<field name="default_picking_policy" attrs="{'invisible':[('group_invoice_deli_orders','=',False)]}"/>
|
||||
</field>
|
||||
<field name="group_discount_per_so_line" position="after">
|
||||
<field name="group_mrp_properties"/>
|
||||
</field>
|
||||
<field name="group_invoice_so_lines" position="replace">
|
||||
<field name="group_invoice_so_lines" on_change="onchange_invoice_methods(group_invoice_so_lines, group_invoice_deli_orders)"/>
|
||||
</field>
|
||||
<field name="group_invoice_so_lines" position="after">
|
||||
<field name="group_invoice_deli_orders" on_change="onchange_invoice_methods(group_invoice_so_lines, group_invoice_deli_orders)"/>
|
||||
</field>
|
||||
<field name="group_invoice_deli_orders" position="after">
|
||||
<field name="task_work" on_change="onchange_task_work(task_work)"/>
|
||||
</field>
|
||||
<field name="default_order_policy" position="replace">
|
||||
<field name="default_order_policy" attrs="{'invisible':['|',('group_invoice_so_lines','=',False),('group_invoice_deli_orders','=',False)],'required': ['|',('group_invoice_so_lines','=',True),('group_invoice_deli_orders','=',True)]}"/>
|
||||
</field>
|
||||
<field name="default_order_policy" position="after">
|
||||
<field name="module_delivery" attrs="{'invisible':[('group_invoice_so_lines','=',False), ('group_invoice_deli_orders','=',False)]}"/>
|
||||
</field>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,639 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from datetime import datetime, timedelta
|
||||
from tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP, float_compare
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from osv import fields, osv
|
||||
import netsvc
|
||||
from tools.translate import _
|
||||
|
||||
class sale_shop(osv.osv):
|
||||
_inherit = "sale.shop"
|
||||
_columns = {
|
||||
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
|
||||
}
|
||||
|
||||
class sale_order(osv.osv):
|
||||
_inherit = "sale.order"
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
if not default:
|
||||
default = {}
|
||||
default.update({
|
||||
'shipped': False,
|
||||
'picking_ids': [],
|
||||
})
|
||||
return super(sale_order, self).copy(cr, uid, id, default, context=context)
|
||||
|
||||
def shipping_policy_change(self, cr, uid, ids, policy, context=None):
|
||||
if not policy:
|
||||
return {}
|
||||
inv_qty = 'order'
|
||||
if policy == 'prepaid':
|
||||
inv_qty = 'order'
|
||||
elif policy == 'picking':
|
||||
inv_qty = 'procurement'
|
||||
return {'value': {'invoice_quantity': inv_qty}}
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
if vals.get('order_policy', False):
|
||||
if vals['order_policy'] == 'prepaid':
|
||||
vals.update({'invoice_quantity': 'order'})
|
||||
elif vals['order_policy'] == 'picking':
|
||||
vals.update({'invoice_quantity': 'procurement'})
|
||||
return super(sale_order, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
if vals.get('order_policy', False):
|
||||
if vals['order_policy'] == 'prepaid':
|
||||
vals.update({'invoice_quantity': 'order'})
|
||||
if vals['order_policy'] == 'picking':
|
||||
vals.update({'invoice_quantity': 'procurement'})
|
||||
order = super(sale_order, self).create(cr, uid, vals, context=context)
|
||||
return order
|
||||
|
||||
# This is False
|
||||
def _picked_rate(self, cr, uid, ids, name, arg, context=None):
|
||||
if not ids:
|
||||
return {}
|
||||
res = {}
|
||||
tmp = {}
|
||||
for id in ids:
|
||||
tmp[id] = {'picked': 0.0, 'total': 0.0}
|
||||
cr.execute('''SELECT
|
||||
p.sale_id as sale_order_id, sum(m.product_qty) as nbr, mp.state as procurement_state, m.state as move_state, p.type as picking_type
|
||||
FROM
|
||||
stock_move m
|
||||
LEFT JOIN
|
||||
stock_picking p on (p.id=m.picking_id)
|
||||
LEFT JOIN
|
||||
procurement_order mp on (mp.move_id=m.id)
|
||||
WHERE
|
||||
p.sale_id IN %s GROUP BY m.state, mp.state, p.sale_id, p.type''', (tuple(ids),))
|
||||
|
||||
for item in cr.dictfetchall():
|
||||
if item['move_state'] == 'cancel':
|
||||
continue
|
||||
|
||||
if item['picking_type'] == 'in':#this is a returned picking
|
||||
tmp[item['sale_order_id']]['total'] -= item['nbr'] or 0.0 # Deducting the return picking qty
|
||||
if item['procurement_state'] == 'done' or item['move_state'] == 'done':
|
||||
tmp[item['sale_order_id']]['picked'] -= item['nbr'] or 0.0
|
||||
else:
|
||||
tmp[item['sale_order_id']]['total'] += item['nbr'] or 0.0
|
||||
if item['procurement_state'] == 'done' or item['move_state'] == 'done':
|
||||
tmp[item['sale_order_id']]['picked'] += item['nbr'] or 0.0
|
||||
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
if order.shipped:
|
||||
res[order.id] = 100.0
|
||||
else:
|
||||
res[order.id] = tmp[order.id]['total'] and (100.0 * tmp[order.id]['picked'] / tmp[order.id]['total']) or 0.0
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'state': fields.selection([
|
||||
('draft', 'Draft Quotation'),
|
||||
('sent', 'Quotation Sent'),
|
||||
('cancel', 'Cancelled'),
|
||||
('waiting_date', 'Waiting Schedule'),
|
||||
('progress', 'Sale Order'),
|
||||
('manual', 'Sale to Invoice'),
|
||||
('shipping_except', 'Shipping Exception'),
|
||||
('invoice_except', 'Invoice Exception'),
|
||||
('done', 'Done'),
|
||||
], 'Status', readonly=True,help="Gives the state of the quotation or sales order.\
|
||||
\nThe exception state is automatically set when a cancel operation occurs \
|
||||
in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception).\nThe 'Waiting Schedule' state is set when the invoice is confirmed\
|
||||
but waiting for the scheduler to run on the order date.", select=True),
|
||||
'incoterm': fields.many2one('stock.incoterms', 'Incoterm', help="Incoterm which stands for 'International Commercial terms' implies its a series of sales terms which are used in the commercial transaction."),
|
||||
'picking_policy': fields.selection([('direct', 'Deliver each product when available'), ('one', 'Deliver all products at once')],
|
||||
'Shipping Policy', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
|
||||
help="""If you don't have enough stock available to deliver all at once, do you accept partial shipments or not?"""),
|
||||
'order_policy': fields.selection([
|
||||
('manual', 'On Demand'),
|
||||
('picking', 'On Delivery Order'),
|
||||
('prepaid', 'Before Delivery'),
|
||||
], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
|
||||
help="""This field controls how invoice and delivery operations are synchronized.
|
||||
- With 'On Demand', the invoice is created manually when needed.
|
||||
- With 'On Delivery Order', a draft invoice is generated after all pickings have been processed.
|
||||
- With 'Before Delivery', a draft invoice is created, and it must be paid before delivery."""),
|
||||
'picking_ids': fields.one2many('stock.picking.out', 'sale_id', 'Related Picking', readonly=True, help="This is a list of delivery orders that has been generated for this sales order."),
|
||||
'shipped': fields.boolean('Delivered', readonly=True, help="It indicates that the sales order has been delivered. This field is updated only after the scheduler(s) have been launched."),
|
||||
'picked_rate': fields.function(_picked_rate, string='Picked', type='float'),
|
||||
'invoice_quantity': fields.selection([('order', 'Ordered Quantities'), ('procurement', 'Shipped Quantities')], 'Invoice on',
|
||||
help="The sale order will automatically create the invoice proposition (draft invoice).\
|
||||
You have to choose if you want your invoice based on ordered ", required=True, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
}
|
||||
_defaults = {
|
||||
'picking_policy': 'direct',
|
||||
'order_policy': 'manual',
|
||||
'invoice_quantity': 'order',
|
||||
}
|
||||
|
||||
# Form filling
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
sale_orders = self.read(cr, uid, ids, ['state'], context=context)
|
||||
unlink_ids = []
|
||||
for s in sale_orders:
|
||||
if s['state'] in ['draft', 'cancel']:
|
||||
unlink_ids.append(s['id'])
|
||||
else:
|
||||
raise osv.except_osv(_('Invalid action !'), _('In order to delete a confirmed sale order, you must cancel it before ! To cancel a sale order, you must first cancel related picking or delivery orders.'))
|
||||
|
||||
return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
|
||||
|
||||
|
||||
|
||||
def action_view_delivery(self, cr, uid, ids, context=None):
|
||||
'''
|
||||
This function returns an action that display existing delivery orders of given sale order ids. It can either be a in a list or in a form view, if there is only one delivery order to show.
|
||||
'''
|
||||
mod_obj = self.pool.get('ir.model.data')
|
||||
result = {
|
||||
'name': _('Delivery Order'),
|
||||
'view_type': 'form',
|
||||
'res_model': 'stock.picking',
|
||||
'context': "{'type':'out'}",
|
||||
'type': 'ir.actions.act_window',
|
||||
'nodestroy': True,
|
||||
'target': 'current',
|
||||
}
|
||||
#compute the number of delivery orders to display
|
||||
pick_ids = []
|
||||
for so in self.browse(cr, uid, ids, context=context):
|
||||
pick_ids += [picking.id for picking in so.picking_ids]
|
||||
#choose the view_mode accordingly
|
||||
if len(pick_ids) > 1:
|
||||
res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_tree')
|
||||
result.update({
|
||||
'view_mode': 'tree,form',
|
||||
'res_id': pick_ids or False
|
||||
})
|
||||
else:
|
||||
res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_form')
|
||||
result.update({
|
||||
'view_mode': 'form',
|
||||
'res_id': pick_ids and pick_ids[0] or False,
|
||||
})
|
||||
result.update(view_id = res and res[1] or False)
|
||||
return result
|
||||
|
||||
def action_wait(self, cr, uid, ids, context=None):
|
||||
for o in self.browse(cr, uid, ids):
|
||||
if not o.order_line:
|
||||
raise osv.except_osv(_('Error !'),_('You cannot confirm a sale order which has no line.'))
|
||||
if (o.order_policy == 'manual'):
|
||||
self.write(cr, uid, [o.id], {'state': 'manual', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})
|
||||
else:
|
||||
self.write(cr, uid, [o.id], {'state': 'progress', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})
|
||||
self.pool.get('sale.order.line').button_confirm(cr, uid, [x.id for x in o.order_line])
|
||||
self.confirm_send_note(cr, uid, ids, context)
|
||||
return True
|
||||
|
||||
def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed', 'done', 'exception'], date_inv = False, context=None):
|
||||
res = False
|
||||
invoices = {}
|
||||
invoice_ids = []
|
||||
picking_obj = self.pool.get('stock.picking')
|
||||
invoice = self.pool.get('account.invoice')
|
||||
obj_sale_order_line = self.pool.get('sale.order.line')
|
||||
partner_currency = {}
|
||||
if context is None:
|
||||
context = {}
|
||||
# If date was specified, use it as date invoiced, usefull when invoices are generated this month and put the
|
||||
# last day of the last month as invoice date
|
||||
if date_inv:
|
||||
context['date_inv'] = date_inv
|
||||
for o in self.browse(cr, uid, ids, context=context):
|
||||
currency_id = o.pricelist_id.currency_id.id
|
||||
if (o.partner_id.id in partner_currency) and (partner_currency[o.partner_id.id] <> currency_id):
|
||||
raise osv.except_osv(
|
||||
_('Error !'),
|
||||
_('You cannot group sales having different currencies for the same partner.'))
|
||||
|
||||
partner_currency[o.partner_id.id] = currency_id
|
||||
lines = []
|
||||
for line in o.order_line:
|
||||
if line.invoiced:
|
||||
continue
|
||||
elif (line.state in states):
|
||||
lines.append(line.id)
|
||||
created_lines = obj_sale_order_line.invoice_line_create(cr, uid, lines)
|
||||
if created_lines:
|
||||
invoices.setdefault(o.partner_id.id, []).append((o, created_lines))
|
||||
if not invoices:
|
||||
for o in self.browse(cr, uid, ids, context=context):
|
||||
for i in o.invoice_ids:
|
||||
if i.state == 'draft':
|
||||
return i.id
|
||||
for val in invoices.values():
|
||||
if grouped:
|
||||
res = self._make_invoice(cr, uid, val[0][0], reduce(lambda x, y: x + y, [l for o, l in val], []), context=context)
|
||||
invoice_ref = ''
|
||||
for o, l in val:
|
||||
invoice_ref += o.name + '|'
|
||||
self.write(cr, uid, [o.id], {'state': 'progress'})
|
||||
if o.order_policy == 'picking':
|
||||
picking_obj.write(cr, uid, map(lambda x: x.id, o.picking_ids), {'invoice_state': 'invoiced'})
|
||||
cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (o.id, res))
|
||||
invoice.write(cr, uid, [res], {'origin': invoice_ref, 'name': invoice_ref})
|
||||
else:
|
||||
for order, il in val:
|
||||
res = self._make_invoice(cr, uid, order, il, context=context)
|
||||
invoice_ids.append(res)
|
||||
self.write(cr, uid, [order.id], {'state': 'progress'})
|
||||
if order.order_policy == 'picking':
|
||||
picking_obj.write(cr, uid, map(lambda x: x.id, order.picking_ids), {'invoice_state': 'invoiced'})
|
||||
cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (order.id, res))
|
||||
if res:
|
||||
self.invoice_send_note(cr, uid, ids, res, context)
|
||||
return res
|
||||
|
||||
def action_cancel(self, cr, uid, ids, context=None):
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
if context is None:
|
||||
context = {}
|
||||
sale_order_line_obj = self.pool.get('sale.order.line')
|
||||
proc_obj = self.pool.get('procurement.order')
|
||||
for sale in self.browse(cr, uid, ids, context=context):
|
||||
for pick in sale.picking_ids:
|
||||
if pick.state not in ('draft', 'cancel'):
|
||||
raise osv.except_osv(
|
||||
_('Could not cancel sales order !'),
|
||||
_('You must first cancel all picking attached to this sales order.'))
|
||||
if pick.state == 'cancel':
|
||||
for mov in pick.move_lines:
|
||||
proc_ids = proc_obj.search(cr, uid, [('move_id', '=', mov.id)])
|
||||
if proc_ids:
|
||||
for proc in proc_ids:
|
||||
wf_service.trg_validate(uid, 'procurement.order', proc, 'button_check', cr)
|
||||
for r in self.read(cr, uid, ids, ['picking_ids']):
|
||||
for pick in r['picking_ids']:
|
||||
wf_service.trg_validate(uid, 'stock.picking', pick, 'button_cancel', cr)
|
||||
return super(sale_order, self).action_cancel(cr, uid, ids, context=context)
|
||||
|
||||
def procurement_lines_get(self, cr, uid, ids, *args):
|
||||
res = []
|
||||
for order in self.browse(cr, uid, ids, context={}):
|
||||
for line in order.order_line:
|
||||
if line.procurement_id:
|
||||
res.append(line.procurement_id.id)
|
||||
return res
|
||||
|
||||
# if mode == 'finished':
|
||||
# returns True if all lines are done, False otherwise
|
||||
# if mode == 'canceled':
|
||||
# returns True if there is at least one canceled line, False otherwise
|
||||
def test_state(self, cr, uid, ids, mode, *args):
|
||||
assert mode in ('finished', 'canceled'), _("invalid mode for test_state")
|
||||
finished = True
|
||||
canceled = False
|
||||
notcanceled = False
|
||||
write_done_ids = []
|
||||
write_cancel_ids = []
|
||||
for order in self.browse(cr, uid, ids, context={}):
|
||||
for line in order.order_line:
|
||||
if (not line.procurement_id) or (line.procurement_id.state=='done'):
|
||||
if line.state != 'done':
|
||||
write_done_ids.append(line.id)
|
||||
else:
|
||||
finished = False
|
||||
if line.procurement_id:
|
||||
if (line.procurement_id.state == 'cancel'):
|
||||
canceled = True
|
||||
if line.state != 'exception':
|
||||
write_cancel_ids.append(line.id)
|
||||
else:
|
||||
notcanceled = True
|
||||
if write_done_ids:
|
||||
self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'})
|
||||
if write_cancel_ids:
|
||||
self.pool.get('sale.order.line').write(cr, uid, write_cancel_ids, {'state': 'exception'})
|
||||
|
||||
if mode == 'finished':
|
||||
return finished
|
||||
elif mode == 'canceled':
|
||||
return canceled
|
||||
if notcanceled:
|
||||
return False
|
||||
return canceled
|
||||
|
||||
def _prepare_order_line_procurement(self, cr, uid, order, line, move_id, date_planned, context=None):
|
||||
return {
|
||||
'name': line.name.split('\n')[0],
|
||||
'origin': order.name,
|
||||
'date_planned': date_planned,
|
||||
'product_id': line.product_id.id,
|
||||
'product_qty': line.product_uom_qty,
|
||||
'product_uom': line.product_uom.id,
|
||||
'product_uos_qty': (line.product_uos and line.product_uos_qty)\
|
||||
or line.product_uom_qty,
|
||||
'product_uos': (line.product_uos and line.product_uos.id)\
|
||||
or line.product_uom.id,
|
||||
'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
|
||||
'procure_method': line.type,
|
||||
'move_id': move_id,
|
||||
'company_id': order.company_id.id,
|
||||
'note': '\n'.join(line.name.split('\n')[1:]),
|
||||
'property_ids': [(6, 0, [x.id for x in line.property_ids])]
|
||||
}
|
||||
|
||||
def _prepare_order_line_move(self, cr, uid, order, line, picking_id, date_planned, context=None):
|
||||
location_id = order.shop_id.warehouse_id.lot_stock_id.id
|
||||
output_id = order.shop_id.warehouse_id.lot_output_id.id
|
||||
return {
|
||||
'name': line.name.split('\n')[0][:250],
|
||||
'picking_id': picking_id,
|
||||
'product_id': line.product_id.id,
|
||||
'date': date_planned,
|
||||
'date_expected': date_planned,
|
||||
'product_qty': line.product_uom_qty,
|
||||
'product_uom': line.product_uom.id,
|
||||
'product_uos_qty': (line.product_uos and line.product_uos_qty) or line.product_uom_qty,
|
||||
'product_uos': (line.product_uos and line.product_uos.id)\
|
||||
or line.product_uom.id,
|
||||
'product_packaging': line.product_packaging.id,
|
||||
'partner_id': line.address_allotment_id.id or order.partner_shipping_id.id,
|
||||
'location_id': location_id,
|
||||
'location_dest_id': output_id,
|
||||
'sale_line_id': line.id,
|
||||
'tracking_id': False,
|
||||
'state': 'draft',
|
||||
#'state': 'waiting',
|
||||
'note': '\n'.join(line.name.split('\n')[1:]),
|
||||
'company_id': order.company_id.id,
|
||||
'price_unit': line.product_id.standard_price or 0.0
|
||||
}
|
||||
|
||||
def _prepare_order_picking(self, cr, uid, order, context=None):
|
||||
pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out')
|
||||
return {
|
||||
'name': pick_name,
|
||||
'origin': order.name,
|
||||
'date': order.date_order,
|
||||
'type': 'out',
|
||||
'state': 'auto',
|
||||
'move_type': order.picking_policy,
|
||||
'sale_id': order.id,
|
||||
'partner_id': order.partner_shipping_id.id,
|
||||
'note': order.note,
|
||||
'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none',
|
||||
'company_id': order.company_id.id,
|
||||
}
|
||||
|
||||
def ship_recreate(self, cr, uid, order, line, move_id, proc_id):
|
||||
# FIXME: deals with potentially cancelled shipments, seems broken (specially if shipment has production lot)
|
||||
"""
|
||||
Define ship_recreate for process after shipping exception
|
||||
param order: sale order to which the order lines belong
|
||||
param line: sale order line records to procure
|
||||
param move_id: the ID of stock move
|
||||
param proc_id: the ID of procurement
|
||||
"""
|
||||
move_obj = self.pool.get('stock.move')
|
||||
if order.state == 'shipping_except':
|
||||
for pick in order.picking_ids:
|
||||
for move in pick.move_lines:
|
||||
if move.state == 'cancel':
|
||||
mov_ids = move_obj.search(cr, uid, [('state', '=', 'cancel'),('sale_line_id', '=', line.id),('picking_id', '=', pick.id)])
|
||||
if mov_ids:
|
||||
for mov in move_obj.browse(cr, uid, mov_ids):
|
||||
# FIXME: the following seems broken: what if move_id doesn't exist? What if there are several mov_ids? Shouldn't that be a sum?
|
||||
move_obj.write(cr, uid, [move_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty})
|
||||
self.pool.get('procurement.order').write(cr, uid, [proc_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty})
|
||||
return True
|
||||
|
||||
def _get_date_planned(self, cr, uid, order, line, start_date, context=None):
|
||||
date_planned = datetime.strptime(start_date, DEFAULT_SERVER_DATE_FORMAT) + relativedelta(days=line.delay or 0.0)
|
||||
date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
return date_planned
|
||||
|
||||
def _create_pickings_and_procurements(self, cr, uid, order, order_lines, picking_id=False, context=None):
|
||||
"""Create the required procurements to supply sale order lines, also connecting
|
||||
the procurements to appropriate stock moves in order to bring the goods to the
|
||||
sale order's requested location.
|
||||
|
||||
If ``picking_id`` is provided, the stock moves will be added to it, otherwise
|
||||
a standard outgoing picking will be created to wrap the stock moves, as returned
|
||||
by :meth:`~._prepare_order_picking`.
|
||||
|
||||
Modules that wish to customize the procurements or partition the stock moves over
|
||||
multiple stock pickings may override this method and call ``super()`` with
|
||||
different subsets of ``order_lines`` and/or preset ``picking_id`` values.
|
||||
|
||||
:param browse_record order: sale order to which the order lines belong
|
||||
:param list(browse_record) order_lines: sale order line records to procure
|
||||
:param int picking_id: optional ID of a stock picking to which the created stock moves
|
||||
will be added. A new picking will be created if ommitted.
|
||||
:return: True
|
||||
"""
|
||||
move_obj = self.pool.get('stock.move')
|
||||
picking_obj = self.pool.get('stock.picking')
|
||||
procurement_obj = self.pool.get('procurement.order')
|
||||
proc_ids = []
|
||||
|
||||
for line in order_lines:
|
||||
if line.state == 'done':
|
||||
continue
|
||||
|
||||
date_planned = self._get_date_planned(cr, uid, order, line, order.date_order, context=context)
|
||||
|
||||
if line.product_id:
|
||||
if line.product_id.product_tmpl_id.type in ('product', 'consu'):
|
||||
if not picking_id:
|
||||
picking_id = picking_obj.create(cr, uid, self._prepare_order_picking(cr, uid, order, context=context))
|
||||
move_id = move_obj.create(cr, uid, self._prepare_order_line_move(cr, uid, order, line, picking_id, date_planned, context=context))
|
||||
else:
|
||||
# a service has no stock move
|
||||
move_id = False
|
||||
|
||||
proc_id = procurement_obj.create(cr, uid, self._prepare_order_line_procurement(cr, uid, order, line, move_id, date_planned, context=context))
|
||||
proc_ids.append(proc_id)
|
||||
line.write({'procurement_id': proc_id})
|
||||
self.ship_recreate(cr, uid, order, line, move_id, proc_id)
|
||||
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
if picking_id:
|
||||
wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
|
||||
self.delivery_send_note(cr, uid, [order.id], picking_id, context)
|
||||
|
||||
|
||||
for proc_id in proc_ids:
|
||||
wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr)
|
||||
|
||||
val = {}
|
||||
if order.state == 'shipping_except':
|
||||
val['state'] = 'progress'
|
||||
val['shipped'] = False
|
||||
|
||||
if (order.order_policy == 'manual'):
|
||||
for line in order.order_line:
|
||||
if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
|
||||
val['state'] = 'manual'
|
||||
break
|
||||
order.write(val)
|
||||
return True
|
||||
|
||||
def action_ship_create(self, cr, uid, ids, context=None):
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
self._create_pickings_and_procurements(cr, uid, order, order.order_line, None, context=context)
|
||||
return True
|
||||
|
||||
def action_ship_end(self, cr, uid, ids, context=None):
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
val = {'shipped': True}
|
||||
if order.state == 'shipping_except':
|
||||
val['state'] = 'progress'
|
||||
if (order.order_policy == 'manual'):
|
||||
for line in order.order_line:
|
||||
if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
|
||||
val['state'] = 'manual'
|
||||
break
|
||||
for line in order.order_line:
|
||||
towrite = []
|
||||
if line.state == 'exception':
|
||||
towrite.append(line.id)
|
||||
if towrite:
|
||||
self.pool.get('sale.order.line').write(cr, uid, towrite, {'state': 'done'}, context=context)
|
||||
res = self.write(cr, uid, [order.id], val)
|
||||
if res:
|
||||
self.delivery_end_send_note(cr, uid, [order.id], context=context)
|
||||
return True
|
||||
|
||||
def has_stockable_products(self, cr, uid, ids, *args):
|
||||
for order in self.browse(cr, uid, ids):
|
||||
for order_line in order.order_line:
|
||||
if order_line.product_id and order_line.product_id.product_tmpl_id.type in ('product', 'consu'):
|
||||
return True
|
||||
return False
|
||||
# ------------------------------------------------
|
||||
# OpenChatter methods and notifications
|
||||
# ------------------------------------------------
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = super(sale_order, self).get_needaction_user_ids(cr, uid, ids, context=context)
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if (obj.state == 'manual' or obj.state == 'progress'):
|
||||
result[obj.id].append(obj.user_id.id)
|
||||
return result
|
||||
|
||||
def delivery_send_note(self, cr, uid, ids, picking_id, context=None):
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
for picking in (pck for pck in order.picking_ids if pck.id == picking_id):
|
||||
# convert datetime field to a datetime, using server format, then
|
||||
# convert it to the user TZ and re-render it with %Z to add the timezone
|
||||
picking_datetime = fields.DT.datetime.strptime(picking.min_date, DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
picking_date_str = fields.datetime.context_timestamp(cr, uid, picking_datetime, context=context).strftime(DATETIME_FORMATS_MAP['%+'] + " (%Z)")
|
||||
self.message_append_note(cr, uid, [order.id], body=_("Delivery Order <em>%s</em> <b>scheduled</b> for %s.") % (picking.name, picking_date_str), context=context)
|
||||
|
||||
def delivery_end_send_note(self, cr, uid, ids, context=None):
|
||||
self.message_append_note(cr, uid, ids, body=_("Order <b>delivered</b>."), context=context)
|
||||
|
||||
|
||||
class sale_order_line(osv.osv):
|
||||
|
||||
def _number_packages(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = {}
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
try:
|
||||
res[line.id] = int((line.product_uom_qty+line.product_packaging.qty-0.0001) / line.product_packaging.qty)
|
||||
except:
|
||||
res[line.id] = 1
|
||||
return res
|
||||
|
||||
_inherit = 'sale.order.line'
|
||||
_columns = {
|
||||
'delay': fields.float('Delivery Lead Time', required=True, help="Number of days between the order confirmation the shipping of the products to the customer", readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'procurement_id': fields.many2one('procurement.order', 'Procurement'),
|
||||
'type': fields.selection([('make_to_stock', 'from stock'), ('make_to_order', 'on order')], 'Procurement Method', required=True, readonly=True, states={'draft': [('readonly', False)]},
|
||||
help="If 'on order', it triggers a procurement when the sale order is confirmed to create a task, purchase order or manufacturing order linked to this sale order line."),
|
||||
'property_ids': fields.many2many('mrp.property', 'sale_order_line_property_rel', 'order_id', 'property_id', 'Properties', readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'product_packaging': fields.many2one('product.packaging', 'Packaging'),
|
||||
'move_ids': fields.one2many('stock.move', 'sale_line_id', 'Inventory Moves', readonly=True),
|
||||
'number_packages': fields.function(_number_packages, type='integer', string='Number Packages'),
|
||||
|
||||
}
|
||||
_defaults = {
|
||||
'delay': 0.0,
|
||||
'type': 'make_to_stock',
|
||||
'product_packaging': False,
|
||||
}
|
||||
|
||||
def _prepare_order_line_invoice_line(self, cr, uid, line, account_id=False, context=None):
|
||||
def _get_line_qty(line):
|
||||
if not (line.order_id.invoice_quantity=='order') or line.procurement_id:
|
||||
return self.pool.get('procurement.order').quantity_get(cr, uid, line.procurement_id.id, context=context)
|
||||
|
||||
def _get_line_uom(line):
|
||||
if not (line.order_id.invoice_quantity=='order') or line.procurement_id:
|
||||
return self.pool.get('procurement.order').uom_get(cr, uid, line.procurement_id.id, context=context)
|
||||
|
||||
return super(sale_order_line, self)._prepare_order_line_invoice_line(cr, uid, line, account_id=account_id, context=context)
|
||||
|
||||
def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False,
|
||||
partner_id=False, packaging=False, flag=False, context=None):
|
||||
if not product:
|
||||
return {'value': {'product_packaging': False}}
|
||||
product_obj = self.pool.get('product.product')
|
||||
product_uom_obj = self.pool.get('product.uom')
|
||||
pack_obj = self.pool.get('product.packaging')
|
||||
warning = {}
|
||||
result = {}
|
||||
warning_msgs = ''
|
||||
if flag:
|
||||
res = self.product_id_change(cr, uid, ids, pricelist=pricelist,
|
||||
product=product, qty=qty, uom=uom, partner_id=partner_id,
|
||||
packaging=packaging, flag=False, context=context)
|
||||
warning_msgs = res.get('warning') and res['warning']['message']
|
||||
|
||||
products = product_obj.browse(cr, uid, product, context=context)
|
||||
if not products.packaging:
|
||||
packaging = result['product_packaging'] = False
|
||||
elif not packaging and products.packaging and not flag:
|
||||
packaging = products.packaging[0].id
|
||||
result['product_packaging'] = packaging
|
||||
|
||||
if packaging:
|
||||
default_uom = products.uom_id and products.uom_id.id
|
||||
pack = pack_obj.browse(cr, uid, packaging, context=context)
|
||||
q = product_uom_obj._compute_qty(cr, uid, uom, pack.qty, default_uom)
|
||||
# qty = qty - qty % q + q
|
||||
if qty and (q and not (qty % q) == 0):
|
||||
ean = pack.ean or _('(n/a)')
|
||||
qty_pack = pack.qty
|
||||
type_ul = pack.ul
|
||||
if not warning_msgs:
|
||||
warn_msg = _("You selected a quantity of %d Units.\n"
|
||||
"But it's not compatible with the selected packaging.\n"
|
||||
"Here is a proposition of quantities according to the packaging:\n"
|
||||
"EAN: %s Quantity: %s Type of ul: %s") % \
|
||||
(qty, ean, qty_pack, type_ul.name)
|
||||
warning_msgs += _("Picking Information ! : ") + warn_msg + "\n\n"
|
||||
warning = {
|
||||
'title': _('Configuration Error !'),
|
||||
'message': warning_msgs
|
||||
}
|
||||
result['product_uom_qty'] = qty
|
||||
|
||||
return {'value': result, 'warning': warning}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<record id="sale.shop" model="sale.shop">
|
||||
<field name="warehouse_id" ref="stock.warehouse0"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="sale.order4" model="sale.order">
|
||||
<field name="order_policy">manual</field>
|
||||
</record>
|
||||
|
||||
<workflow action="order_confirm" model="sale.order" ref="sale.order4"/>
|
||||
|
||||
<record id="sale.order5" model="sale.order">
|
||||
<field name="order_policy">manual</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.order6" model="sale.order">
|
||||
<field name="order_policy">manual</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.order7" model="sale.order">
|
||||
<field name="order_policy">manual</field>
|
||||
</record>
|
||||
|
||||
<workflow action="order_confirm" model="sale.order" ref="sale.order5"/>
|
||||
|
||||
<record id="sale.order" model="sale.order">
|
||||
<field name="order_policy">picking</field>
|
||||
<field name="invoice_quantity">procurement</field>
|
||||
<field name="note">Invoice after delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.line" model="sale.order.line">
|
||||
<field name="type">make_to_stock</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.line1" model="sale.order.line">
|
||||
<field name="type">make_to_stock</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.line3" model="sale.order.line">
|
||||
<field name="type">make_to_order</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.line4" model="sale.order.line">
|
||||
<field name="type">make_to_order</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.line5" model="sale.order.line">
|
||||
<field name="delay">2</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.line6" model="sale.order.line">
|
||||
<field name="delay">7</field>
|
||||
<field name="type">make_to_order</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.line7" model="sale.order.line">
|
||||
<field name="delay">8</field>
|
||||
<field name="type">make_to_order</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.line8" model="sale.order.line">
|
||||
<field name="type">make_to_order</field>
|
||||
<field name="delay">1</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.line9" model="sale.order.line">
|
||||
<field name="type">make_to_order</field>
|
||||
<field name="delay">15</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.line12" model="sale.order.line">
|
||||
<field name="type">make_to_stock</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.line13" model="sale.order.line">
|
||||
<field name="delay">1</field>
|
||||
<field name="type">make_to_stock</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="sale.line52" model="sale.order.line">
|
||||
<field name="type">make_to_stock</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.order6_line0" model="sale.order.line">
|
||||
<field name="type">make_to_order</field>
|
||||
<field name="delay">15</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.order6_line1" model="sale.order.line">
|
||||
<field name="type">make_to_order</field>
|
||||
<field name="delay">15</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.order7_line0" model="sale.order.line">
|
||||
<field name="type">make_to_stock</field>
|
||||
<field name="delay">15</field>
|
||||
</record>
|
||||
|
||||
<record id="sale.order7_line1" model="sale.order.line">
|
||||
<field name="type">make_to_stock</field>
|
||||
<field name="delay">15</field>
|
||||
</record>
|
||||
|
||||
<!-- Run all schedulers -->
|
||||
<function model="procurement.order" name="run_scheduler"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,150 @@
|
|||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- <record id="view_shop_form_inherit" model="ir.ui.view">
|
||||
<field name="name">sale.shop.sale.stock</field>
|
||||
<field name="model">sale.shop</field>
|
||||
<field name="inherit_id" ref="sale.view_shop_form"/>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="/form/label/h1/group[@name='shop']" position="before">
|
||||
|
||||
<label for="warehouse_id" class="oe_edit_only"/>
|
||||
<h2><field name="warehouse_id" required="1"/></h2>
|
||||
</xpath>
|
||||
</field>
|
||||
</record> -->
|
||||
|
||||
<record id="view_shop_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">sale.shop.sale.stock</field>
|
||||
<field name="model">sale.shop</field>
|
||||
<field name="inherit_id" ref="sale.view_shop_tree"/>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="after">
|
||||
<field name="warehouse_id"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="view_order_tree_sale_stock_inherit" model="ir.ui.view">
|
||||
<field name="name">sale.order.tree.sale.stock</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_tree"/>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="user_id" position="after">
|
||||
<field name="picked_rate" widget="progressbar"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_order_form_inherit" model="ir.ui.view">
|
||||
<field name="name">sale.order.form.sale.stock</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="/form/header/span/button[@name='invoice_corrected']" position="after">
|
||||
<button name="ship_recreate" states="shipping_except" string="Recreate Delivery Order"/>
|
||||
<button name="ship_corrected" states="shipping_except" string="Ignore Exception"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/header/span/button[@name='action_view_invoice']" position="replace">
|
||||
<button name="action_view_invoice" string="Open Invoice" type="object" class="oe_highlight"
|
||||
attrs="{'invisible': ['|','|',('state', '!=','progress'), ('invoiced', '=', True),('order_policy','=','picking')]}"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/header/span/button[@name='action_view_invoice']" position="after">
|
||||
<button name="action_view_delivery" string="Open Delivery Order" type="object" class="oe_highlight"
|
||||
attrs="{'invisible': ['|','|','|',('picking_ids','=',False),('picking_ids','=',[]), ('state', 'not in', ('progress','manual')),('shipped','=',True)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/header/span/button[@name='action_cancel']" position="after">
|
||||
<button name="ship_cancel" states="shipping_except" string="Cancel"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/header/span/button[@name='action_view_delivery']" position="after">
|
||||
<button name="%(action_view_sale_advance_payment_inv)d" string="Create Invoice" type="action" states="manual"/>
|
||||
</xpath>
|
||||
<field name="state" position="replace">
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,sent,progress,invoiced,done" statusbar_colors='{"shipping_except":"red","invoice_except":"red","waiting_date":"blue"}'/>
|
||||
</field>
|
||||
<field name="product_id" position="replace">
|
||||
<field name="product_id"
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}"
|
||||
groups="base.group_user"
|
||||
on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, False, False, parent.date_order, product_packaging, parent.fiscal_position, True, context)"/>
|
||||
</field>
|
||||
<field name="product_uom_qty" position="replace">
|
||||
<field context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}"
|
||||
name="product_uom_qty" class="oe_inline"
|
||||
on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, False, False, parent.date_order, product_packaging, parent.fiscal_position, True, context)"/>
|
||||
</field>
|
||||
<field name="shop_id" position="replace">
|
||||
<field name="shop_id" on_change="onchange_shop_id(shop_id)" widget="selection" groups="stock.group_locations"/>
|
||||
</field>
|
||||
|
||||
<field name="order_policy" position="before">
|
||||
<field name="incoterm" widget="selection" groups="base.group_user"/>
|
||||
</field>
|
||||
<field name="incoterm" position="after">
|
||||
<field name="picking_policy" required="True"/>
|
||||
</field>
|
||||
<!-- <xpath expr="//page[@string='Other Information']/group/group/field[@name='invoiced']" position="after">
|
||||
<field name="shipped"/>
|
||||
</xpath> -->
|
||||
<field name="invoice_ids" position="after">
|
||||
<separator string="Delivery Orders"/>
|
||||
<field name="picking_ids" context="{'default_type': 'out'}">
|
||||
<tree colors="blue:state == 'draft';grey:state == 'cancel';red:state not in ('cancel', 'done') and min_date < current_date" string="Delivery Orders">
|
||||
<field name="name"/>
|
||||
<field name="partner_id" string="Customer"/>
|
||||
<field name="min_date"/>
|
||||
<field name="state"/>
|
||||
<button name="action_process" states="assigned" string="Deliver" type="object" icon="gtk-go-forward" context="{'default_type': 'out'}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</field>
|
||||
<field name="th_weight" position="before">
|
||||
<label for="delay"/>
|
||||
<div>
|
||||
<field name="delay" class="oe_inline"/> days
|
||||
</div>
|
||||
<field name="type"/>
|
||||
</field>
|
||||
<field name="address_allotment_id" position="after">
|
||||
<field name="property_ids" widget="many2many_tags"
|
||||
groups="sale.group_mrp_properties"/>
|
||||
</field>
|
||||
<field name="tax_id" position="before">
|
||||
<field name="product_packaging" context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}" on_change="product_packaging_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, parent.partner_id, product_packaging, True, context)" domain="[('product_id','=',product_id)]" groups="product.group_stock_packaging" colspan="3"/>
|
||||
</field>
|
||||
<field name="invoice_lines" position="after">
|
||||
<label for="move_ids"/>
|
||||
<field name="move_ids" widget="many2many"/>
|
||||
</field>
|
||||
<xpath expr="//page[@string='Order Lines']/field[@name='order_line']/tree[@string='Sales Order Lines']/field[@name='sequence']" position="after">
|
||||
<field name="delay" invisible="1"/>
|
||||
|
||||
</xpath>
|
||||
<xpath expr="//page[@string='Order Lines']/field[@name='order_line']/tree[@string='Sales Order Lines']/field[@name='tax_id']" position="after">
|
||||
<field name="type" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@string='Order Lines']/field[@name='order_line']/tree[@string='Sales Order Lines']/field[@name='th_weight']" position="after">
|
||||
<field name="product_packaging" invisible="1"/>
|
||||
</xpath>
|
||||
<!-- <xpath expr="//page[@string='Other Information']/field[@name='order_line']/group/group[@name='logistics']/field[@name='order_policy']" position="before">
|
||||
<field name="incoterm" widget="selection" groups="base.group_user"/>
|
||||
<field name="picking_policy" required="True"/>
|
||||
</xpath>-->
|
||||
<!-- <xpath expr="//page[@string='Other Information']/field[@name='order_line']/group/group/field[@name='invoiced']" position="after">
|
||||
<field name="shipped"/>
|
||||
</xpath> -->
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="sale.action_shop_form" id="sale.menu_action_shop_form" parent="base.menu_base_config" sequence="35" groups="stock.group_locations"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,125 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Activity -->
|
||||
|
||||
<record id="act_wait_ship" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">wait_ship</field>
|
||||
</record>
|
||||
|
||||
<record id="act_cancel3" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">cancel3</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="kind">stopall</field>
|
||||
<field name="action">action_cancel()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_ship" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">ship</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">action_ship_create()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_ship_except" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">ship_except</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'state':'shipping_except'})</field>
|
||||
</record>
|
||||
|
||||
<record id="act_ship_end" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">ship_end</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">action_ship_end()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_ship_cancel" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">ship_cancel</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="kind">stopall</field>
|
||||
<field name="action">action_cancel()</field>
|
||||
</record>
|
||||
|
||||
<!-- Transistion -->
|
||||
|
||||
<record id="trans_router_wait_ship" model="workflow.transition">
|
||||
<field name="act_from" ref="sale.act_router"/>
|
||||
<field name="act_to" ref="act_wait_ship"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="trans_router_wait_invoice" model="workflow.transition">
|
||||
<field name="act_from" ref="sale.act_router"/>
|
||||
<field name="act_to" ref="sale.act_wait_invoice"/>
|
||||
</record>
|
||||
|
||||
<record id="trans_router_wait_invoice_shipping" model="workflow.transition">
|
||||
<field name="act_from" ref="sale.act_wait_invoice"/>
|
||||
<field name="act_to" ref="sale.act_invoice_end"/>
|
||||
<field name="condition">(order_policy=='picking')</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_wait_ship_cancel3" model="workflow.transition">
|
||||
<field name="act_from" ref="act_wait_ship"/>
|
||||
<field name="act_to" ref="act_cancel3"/>
|
||||
<field name="signal">cancel</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_wait_ship_ship" model="workflow.transition">
|
||||
<field name="act_from" ref="act_wait_ship"/>
|
||||
<field name="act_to" ref="act_ship"/>
|
||||
<field name="condition">(order_policy!='prepaid') or invoiced</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_wait_invoice_invoice_manual" model="workflow.transition">
|
||||
<field name="act_from" ref="sale.act_wait_invoice"/>
|
||||
<field name="act_to" ref="sale.act_invoice"/>
|
||||
<field name="signal">manual_invoice</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_ship_end_done" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship_end"/>
|
||||
<field name="act_to" ref="sale.act_done"/>
|
||||
</record>
|
||||
|
||||
<record id="trans_ship_ship_end" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship"/>
|
||||
<field name="act_to" ref="act_ship_end"/>
|
||||
<field name="trigger_model">procurement.order</field>
|
||||
<field name="trigger_expr_id">procurement_lines_get()</field>
|
||||
<field name="condition">test_state('finished')</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_ship_ship_except" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship"/>
|
||||
<field name="act_to" ref="act_ship_except"/>
|
||||
<field name="condition">test_state('canceled')</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="trans_ship_except_ship" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship_except"/>
|
||||
<field name="act_to" ref="act_ship"/>
|
||||
<field name="signal">ship_recreate</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_ship_except_ship_end" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship_except"/>
|
||||
<field name="act_to" ref="act_ship_end"/>
|
||||
<field name="signal">ship_corrected</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_ship_except_ship_cancel" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship_except"/>
|
||||
<field name="act_to" ref="act_ship_cancel"/>
|
||||
<field name="signal">ship_cancel</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,17 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_stock_picking_salesman,stock_picking salesman,stock.model_stock_picking,base.group_sale_salesman,1,1,1,0
|
||||
access_stock_move_salesman,stock_move salesman,stock.model_stock_move,base.group_sale_salesman,1,1,1,0
|
||||
access_stock_move_manager,stock_move manager,stock.model_stock_move,base.group_sale_manager,1,1,1,1
|
||||
access_procurement_salesman,procurement.order salesman,procurement.model_procurement_order,base.group_sale_salesman,1,1,1,0
|
||||
access_procurement_manager,procurement.order manager,procurement.model_procurement_order,base.group_sale_manager,1,1,1,1
|
||||
access_sale_order_stock_worker,sale.order stock worker,model_sale_order,stock.group_stock_user,1,1,0,0
|
||||
access_sale_order_line_stock_worker,sale.order.line stock worker,model_sale_order_line,stock.group_stock_user,1,1,0,0
|
||||
access_report_stock_move_sales,report.stock.move.sale,stock.model_report_stock_move,base.group_sale_manager,1,1,1,1
|
||||
access_stock_picking_sales,stock.picking.sales,stock.model_stock_picking,base.group_sale_manager,1,1,1,1
|
||||
access_product_packaging_user,product.packaging.user,product.model_product_packaging,base.group_sale_salesman,1,1,1,0
|
||||
access_product_packaging_manager,product.packaging.manager,product.model_product_packaging,base.group_sale_manager,1,0,0,0
|
||||
access_stock_warehouse_user,stock.warehouse.user,stock.model_stock_warehouse,base.group_sale_salesman,1,0,0,0
|
||||
access_stock_location_user,stock.location.user,stock.model_stock_location,base.group_sale_salesman,1,0,0,0
|
||||
access_product_packaging_sale_manager,product.packaging salemanager,product.model_product_packaging,base.group_sale_manager,1,1,1,1
|
||||
access_stock_warehouse_orderpoint_sale_salesman,stock.warehouse.orderpoint,procurement.model_stock_warehouse_orderpoint,base.group_sale_salesman,1,0,0,0
|
||||
access_account_move_line_salesman,account.move.line invoice,account.model_account_move_line,base.group_sale_salesman,1,0,0,0
|
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="0">
|
||||
|
||||
<record id="group_invoice_deli_orders" model="res.groups">
|
||||
<field name="name">Enable Invoicing Delivery orders</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="group_mrp_properties" model="res.groups">
|
||||
<field name="name">Properties on lines</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -77,7 +77,7 @@
|
|||
<field name="context">{'default_type': 'out', 'contact_display': 'partner_address', 'search_default_to_invoice': 1, 'search_default_done': 1, 'default_invoice_state': '2binvoiced'}</field>
|
||||
<field name="search_view_id" ref="stock.view_picking_out_search"/>
|
||||
</record>
|
||||
<menuitem action="outgoing_picking_list_to_invoice" id="menu_action_picking_list_to_invoice" parent="base.menu_invoiced" groups="sale.group_invoice_deli_orders" sequence="20"/>
|
||||
<menuitem action="outgoing_picking_list_to_invoice" id="menu_action_picking_list_to_invoice" parent="base.menu_invoiced" groups="group_invoice_deli_orders" sequence="20"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,42 @@
|
|||
-
|
||||
In order to test the cancel sale order.
|
||||
First I confirm order.
|
||||
-
|
||||
!workflow {model: sale.order, action: order_confirm, ref: sale.order5}
|
||||
-
|
||||
Now I regenerate shipment.
|
||||
-
|
||||
!workflow {model: sale.order, action: ship_recreate, ref: sale.order5}
|
||||
-
|
||||
I check state of order in 'To Invoice'.
|
||||
-
|
||||
!assert {model: sale.order, id: sale.order5, string: Sale order should be In Progress state}:
|
||||
- state == 'manual'
|
||||
-
|
||||
I make invoice for order.
|
||||
-
|
||||
!workflow {model: sale.order, action: manual_invoice, ref: sale.order5}
|
||||
-
|
||||
To cancel the sale order from Invoice Exception, I have to cancel the invoice of sale order.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
import netsvc
|
||||
invoice_ids = self.browse(cr, uid, ref("sale.order5")).invoice_ids
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
first_invoice_id = invoice_ids[0]
|
||||
wf_service.trg_validate(uid, 'account.invoice', first_invoice_id.id, 'invoice_cancel', cr)
|
||||
-
|
||||
I check order status in "Invoice Exception" and related invoice is in cancel state.
|
||||
-
|
||||
!assert {model: sale.order, id: sale.order5, string: Sale order should be in Invoice Exception state}:
|
||||
- state == "invoice_except", "Order should be in Invoice Exception state after cancel Invoice"
|
||||
-
|
||||
Then I click on the Ignore Exception button.
|
||||
-
|
||||
!workflow {model: sale.order, action: invoice_corrected, ref: sale.order5}
|
||||
|
||||
-
|
||||
I check state of order in 'In Progress'.
|
||||
-
|
||||
!assert {model: sale.order, id: sale.order5, string: Sale order should be In progress state}:
|
||||
- state == 'progress'
|
|
@ -1,12 +1,12 @@
|
|||
-
|
||||
I confirm the Quotation with "Deliver & invoice on demand".
|
||||
-
|
||||
!workflow {model: sale.order, action: order_confirm, ref: order4}
|
||||
!workflow {model: sale.order, action: order_confirm, ref: sale.order4}
|
||||
-
|
||||
I check that Invoice should not created.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
sale_order = self.browse(cr, uid, ref("order4"))
|
||||
sale_order = self.browse(cr, uid, ref("sale.order4"))
|
||||
assert len(sale_order.invoice_ids) == False, "Invoice should not created."
|
||||
assert sale_order.picking_ids, "Delivery order should be created."
|
||||
-
|
||||
|
@ -14,28 +14,28 @@
|
|||
-
|
||||
!python {model: sale.advance.payment.inv}: |
|
||||
ctx = context.copy()
|
||||
ctx.update({"active_model": 'sale.order', "active_ids": [ref("order4")], "active_id":ref("order4")})
|
||||
order_line = self.pool.get('sale.order.line').browse(cr, uid, ref("line7"), context=context)
|
||||
ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale.order4")], "active_id":ref("sale.order4")})
|
||||
order_line = self.pool.get('sale.order.line').browse(cr, uid, ref("sale.line7"), context=context)
|
||||
pay_id = self.create(cr, uid, {'advance_payment_method': 'fixed', 'product_id': order_line.product_id.id, 'amount': order_line.price_subtotal, 'qtty': order_line.product_uom_qty})
|
||||
self.create_invoices(cr, uid, [pay_id], context=ctx)
|
||||
-
|
||||
I check Invoice which made advance
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
order = self.browse(cr, uid, ref('order4'))
|
||||
order = self.browse(cr, uid, ref('sale.order4'))
|
||||
assert order.invoice_ids, "Invoice should be created after make advance invoice."
|
||||
-
|
||||
I create Invoice from sale order line.
|
||||
-
|
||||
!python {model: sale.order.line.make.invoice}: |
|
||||
ctx = context.copy()
|
||||
ctx.update({"active_model": 'sale.order.line', "active_ids": [ref("line8")], "active_id":ref("line8")})
|
||||
ctx.update({"active_model": 'sale.order.line', "active_ids": [ref("sale.line8")], "active_id":ref("sale.line8")})
|
||||
self.make_invoices(cr, uid, [], context=ctx)
|
||||
-
|
||||
I check Invoice which made from sale order line.
|
||||
-
|
||||
!python {model: sale.order.line}: |
|
||||
line = self.browse(cr, uid, ref('line8'))
|
||||
line = self.browse(cr, uid, ref('sale.line8'))
|
||||
assert line.invoiced, "Line is not invoiced."
|
||||
assert line.invoice_lines, "Invoice line should be created."
|
||||
-
|
||||
|
@ -46,7 +46,7 @@
|
|||
-
|
||||
!python {model: sale.make.invoice}: |
|
||||
ctx = context.copy()
|
||||
ctx = ctx.update({"active_model": 'sale.order', "active_ids": [ref("order4")], "active_id":ref("order4")})
|
||||
ctx = ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale.order4")], "active_id":ref("sale.order4")})
|
||||
self.make_invoices(cr, uid, [ref("sale_make_invoice_1")], context)
|
||||
-
|
||||
I open the Invoice.
|
||||
|
@ -54,7 +54,7 @@
|
|||
!python {model: sale.order}: |
|
||||
import netsvc
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
so = self.browse(cr, uid, ref("order4"))
|
||||
so = self.browse(cr, uid, ref("sale.order4"))
|
||||
for invoice in so.invoice_ids:
|
||||
wf_service.trg_validate(uid, 'account.invoice', invoice.id, 'invoice_open', cr)
|
||||
-
|
||||
|
@ -62,7 +62,7 @@
|
|||
-
|
||||
!python {model: account.invoice}: |
|
||||
sale_order = self.pool.get('sale.order')
|
||||
order = sale_order.browse(cr, uid, ref("order4"))
|
||||
order = sale_order.browse(cr, uid, ref("sale.order4"))
|
||||
journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'cash'), ('company_id', '=', order.company_id.id)], limit=1)
|
||||
for invoice in order.invoice_ids:
|
||||
invoice.pay_and_reconcile(
|
||||
|
@ -74,7 +74,7 @@
|
|||
I check Invoice after do manual.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
sale_order = self.browse(cr, uid, ref("order4"))
|
||||
sale_order = self.browse(cr, uid, ref("sale.order4"))
|
||||
assert sale_order.invoice_ids, "Invoice should be created."
|
||||
assert sale_order.invoiced, "Order is not invoiced."
|
||||
assert sale_order.state == 'manual', 'Order should be in Manual.'
|
|
@ -3,17 +3,17 @@
|
|||
-
|
||||
First I check the total amount of the Quotation before Approved.
|
||||
-
|
||||
!assert {model: sale.order, id: order, string: The amount of the Quotation is not correctly computed}:
|
||||
!assert {model: sale.order, id: sale.order, string: The amount of the Quotation is not correctly computed}:
|
||||
- sum([l.price_subtotal for l in order_line]) == amount_untaxed
|
||||
-
|
||||
I confirm the quotation with "Invoice based on deliveries" policy.
|
||||
-
|
||||
!workflow {model: sale.order, action: order_confirm, ref: order}
|
||||
!workflow {model: sale.order, action: order_confirm, ref: sale.order}
|
||||
-
|
||||
I check that invoice should not created before dispatch delivery.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
order = self.pool.get('sale.order').browse(cr, uid, ref("order"))
|
||||
order = self.pool.get('sale.order').browse(cr, uid, ref("sale.order"))
|
||||
assert order.state == 'progress', 'Order should be in inprogress.'
|
||||
assert len(order.invoice_ids) == False, "Invoice should not created."
|
||||
-
|
||||
|
@ -23,7 +23,7 @@
|
|||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
||||
order = self.browse(cr, uid, ref("order"))
|
||||
order = self.browse(cr, uid, ref("sale.order"))
|
||||
for order_line in order.order_line:
|
||||
procurement = order_line.procurement_id
|
||||
date_planned = datetime.strptime(order.date_order, DEFAULT_SERVER_DATE_FORMAT) + relativedelta(days=order_line.delay or 0.0)
|
||||
|
@ -45,7 +45,7 @@
|
|||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
||||
sale_order = self.browse(cr, uid, ref("order"))
|
||||
sale_order = self.browse(cr, uid, ref("sale.order"))
|
||||
assert sale_order.picking_ids, "Delivery order is not created."
|
||||
for picking in sale_order.picking_ids:
|
||||
assert picking.state == "auto" or "confirmed", "Delivery order should be in 'Waitting Availability' state."
|
||||
|
@ -77,7 +77,7 @@
|
|||
Now, I dispatch delivery order.
|
||||
-
|
||||
!python {model: stock.partial.picking}: |
|
||||
order = self.pool.get('sale.order').browse(cr, uid, ref("order"))
|
||||
order = self.pool.get('sale.order').browse(cr, uid, ref("sale.order"))
|
||||
for pick in order.picking_ids:
|
||||
data = pick.force_assign()
|
||||
if data == True:
|
||||
|
@ -87,7 +87,7 @@
|
|||
I check sale order to verify shipment.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
order = self.pool.get('sale.order').browse(cr, uid, ref("order"))
|
||||
order = self.pool.get('sale.order').browse(cr, uid, ref("sale.order"))
|
||||
assert order.shipped == True, "Sale order is not Delivered."
|
||||
assert order.picked_rate == 100, "Shipment progress is not 100%."
|
||||
#assert order.state == 'progress', 'Order should be in inprogress.'
|
||||
|
@ -97,7 +97,7 @@
|
|||
-
|
||||
!python {model: stock.invoice.onshipping}: |
|
||||
sale = self.pool.get('sale.order')
|
||||
sale_order = sale.browse(cr, uid, ref("order"))
|
||||
sale_order = sale.browse(cr, uid, ref("sale.order"))
|
||||
ship_ids = [x.id for x in sale_order.picking_ids]
|
||||
wiz_id = self.create(cr, uid, {'journal_id': ref('account.sales_journal')},
|
||||
{'active_ids': ship_ids, 'active_model': 'stock.picking'})
|
||||
|
@ -106,7 +106,7 @@
|
|||
I check the invoice details after dispatched delivery.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
order = self.browse(cr, uid, ref("order"))
|
||||
order = self.browse(cr, uid, ref("sale.order"))
|
||||
assert order.invoice_ids, "Invoice is not created."
|
||||
ac = order.partner_id.property_account_receivable.id
|
||||
journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'sale'), ('company_id', '=', order.company_id.id)])
|
||||
|
@ -134,7 +134,7 @@
|
|||
!python {model: sale.order}: |
|
||||
import netsvc
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
so = self.browse(cr, uid, ref("order"))
|
||||
so = self.browse(cr, uid, ref("sale.order"))
|
||||
for invoice in so.invoice_ids:
|
||||
wf_service.trg_validate(uid, 'account.invoice', invoice.id, 'invoice_open', cr)
|
||||
-
|
||||
|
@ -142,7 +142,7 @@
|
|||
-
|
||||
!python {model: account.invoice}: |
|
||||
sale_order = self.pool.get('sale.order')
|
||||
order = sale_order.browse(cr, uid, ref("order"))
|
||||
order = sale_order.browse(cr, uid, ref("sale.order"))
|
||||
journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'cash'), ('company_id', '=', order.company_id.id)], limit=1)
|
||||
for invoice in order.invoice_ids:
|
||||
invoice.pay_and_reconcile(
|
||||
|
@ -154,7 +154,7 @@
|
|||
I check the order after paid invoice.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
order = self.browse(cr, uid, ref("order"))
|
||||
order = self.browse(cr, uid, ref("sale.order"))
|
||||
assert order.invoiced == True, "Sale order is not invoiced."
|
||||
assert order.invoiced_rate == 100, "Invoiced progress is not 100%."
|
||||
assert order.state == 'done', 'Order should be in closed.'
|
||||
|
@ -163,7 +163,7 @@
|
|||
-
|
||||
!python {model: sale.order}: |
|
||||
import netsvc, tools, os
|
||||
(data, format) = netsvc.LocalService('report.sale.order').create(cr, uid, [ref('order')], {}, {})
|
||||
(data, format) = netsvc.LocalService('report.sale.order').create(cr, uid, [ref('sale.order')], {}, {})
|
||||
if tools.config['test_report_directory']:
|
||||
file(os.path.join(tools.config['test_report_directory'], 'sale-sale_order.'+format), 'wb+').write(data)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import sale_make_invoice
|
||||
import sale_make_invoice_advance
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
Loading…
Reference in New Issue