[MERGE] merged the branch that split sale and sale_stock

bzr revid: qdp-launchpad@openerp.com-20120921095518-leorwvtc5kppv6dz
This commit is contained in:
Quentin (OpenERP) 2012-09-21 11:55:18 +02:00
commit bc72e3da24
109 changed files with 142554 additions and 2987 deletions

View File

@ -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'],
'data': ['event_sale_view.xml'],
'demo': ['event_demo.xml'],
'test': ['test/confirm.yml'],

View File

@ -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',
],
'data': ['res_company_view.xml'],

View File

@ -49,7 +49,7 @@ Main Features
""",
'author': 'OpenERP SA',
'images': ['images/cash_registers.jpeg', 'images/pos_analysis.jpeg','images/register_analysis.jpeg','images/sale_order_pos.jpeg','images/product_pos.jpeg'],
'depends': ['sale'],
'depends': ['sale_stock'],
'data': [
'security/point_of_sale_security.xml',
'security/ir.model.access.csv',

View File

@ -30,7 +30,7 @@ This module adds sale menu and features to your portal if sale and portal are in
========================================================================================
""",
'author': 'OpenERP SA',
'depends': ['sale','portal'],
'depends': ['sale_stock','portal'],
'data': [
'security/portal_security.xml',
'portal_sale_view.xml',

View File

@ -60,7 +60,7 @@ depending on the product's configuration.
'demo': ['stock_orderpoint.xml'],
'test': ['test/procurement.yml'],
'installable': True,
'auto_install': False,
'auto_install': True,
'certificate': '00954248826881074509',
'images': ['images/compute_schedulers.jpeg','images/config_companies_sched.jpeg', 'images/minimum_stock_rules.jpeg'],
}

View File

@ -51,7 +51,7 @@
<field eval="&quot;&quot;&quot;Procurement Task&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;if product type is 'service' then it creates the task.&quot;&quot;&quot;" 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">

View File

@ -24,11 +24,9 @@
#----------------------------------------------------------
import sale
import stock
import res_partner
import wizard
import report
import company
import edi
import res_config

View File

@ -35,8 +35,10 @@ It handles the full sales workflow:
* **Quotation** -> **Sales order** -> **Invoice**
Preferences
Preferences (only with Warehouse Management installed)
-----------
If you also installed the Warehouse Management, you can deal with the following preferences:
* Shipping: Choice of delivery at once or partial delivery
* Invoicing: choose how invoices will be paid
* Incoterms: International Commercial terms
@ -55,15 +57,14 @@ The Dashboard for the Sales Manager will include
""",
'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'],
'images': ['images/sale_dashboard.jpeg','images/Sale_order_line_to_invoice.jpeg','images/sale_order.jpeg','images/sales_analysis.jpeg'],
'depends': ['account_voucher'],
'data': [
'wizard/sale_make_invoice_advance.xml',
'wizard/sale_line_invoice.xml',
'wizard/sale_make_invoice.xml',
'security/sale_security.xml',
'security/ir.model.access.csv',
'company_view.xml',
'sale_workflow.xml',
'sale_sequence.xml',
'sale_report.xml',
@ -71,7 +72,6 @@ The Dashboard for the Sales Manager will include
'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',
@ -80,9 +80,7 @@ The Dashboard for the Sales Manager will include
'demo': ['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',
'test/edi_sale_order.yml',

View File

@ -185,16 +185,9 @@ 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
# discard web preview fields, if present
order_line.pop('price_subtotal', None)
@ -216,11 +209,6 @@ class sale_order_line(osv.osv, EDIMixin):
edi_doc.update(product_uom=line.product_uos,
product_qty=line.product_uos_qty)
# 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)
edi_doc_list.append(edi_doc)
return edi_doc_list

View File

@ -1,15 +1,6 @@
<?xml version="1.0" ?>
<openerp>
<data>
<!-- EDI Export + Send email Action -->
<record id="ir_actions_server_edi_sale" model="ir.actions.server">
<field name="code">if not object.partner_id.opt_out: object.edi_export_and_email(template_ext_id='sale.email_template_edi_sale', context=context)</field>
<field name="state">code</field>
<field name="type">ir.actions.server</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="condition">True</field>
<field name="name">Auto-email confirmed sale orders</field>
</record>
<!-- EDI related Email Templates menu -->
<record model="ir.actions.act_window" id="action_email_templates">
@ -25,14 +16,9 @@
</data>
<!-- Mail template and workflow bindings are done in a NOUPDATE block
<!-- Mail template is 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 -->
<record id="sale.act_wait_ship" model="workflow.activity">
<field name="action_id" ref="ir_actions_server_edi_sale"/>
</record>
<!--Email template -->
<record id="email_template_edi_sale" model="email.template">

File diff suppressed because it is too large Load Diff

View File

@ -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="&quot;&quot;&quot;subflow&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;Sales Order Requisition&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;Drives procurement orders for every sales order line.&quot;&quot;&quot;" name="note"/>
<field name="subflow_id" ref="process_process_salesprocess0"/>
<field name="process_id" ref="procurement.process_process_procurementprocess0"/>
<field eval="&quot;&quot;&quot;object.state in ('draft', 'waiting_date', 'manual', 'progress', 'shipping_except', 'invoice_except', 'done', 'cancel')&quot;&quot;&quot;" 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="&quot;&quot;&quot;subflow&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;Procurement Order&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;One Procurement order for each sales order line and for each of the components.&quot;&quot;&quot;" name="note"/>
<field name="subflow_id" ref="procurement.process_process_procurementprocess0"/>
<field name="process_id" ref="process_process_salesprocess0"/>
<field eval="&quot;&quot;&quot;object.state=='confirmed'&quot;&quot;&quot;" 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="&quot;&quot;&quot;state&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;Pick List&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;Document of the move to the output or to the customer.&quot;&quot;&quot;" name="note"/>
<field name="process_id" ref="process_process_salesprocess0"/>
<field eval="&quot;&quot;&quot;object.state in ('assigned', 'confirmed')&quot;&quot;&quot;" 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="&quot;&quot;&quot;state&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;Delivery Order&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;Document of the move to the customer.&quot;&quot;&quot;" name="note"/>
<field name="process_id" ref="process_process_salesprocess0"/>
<field eval="&quot;&quot;&quot;object.state in ('done', 'assigned')&quot;&quot;&quot;" 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"/>
@ -106,29 +62,6 @@
<field eval="0" name="flow_start"/>
</record>
<record id="process_node_invoiceafterdelivery0" model="process.node">
<field name="menu_id" ref="account.menu_action_invoice_tree1"/>
<field name="model_id" ref="account.model_account_invoice"/>
<field eval="&quot;&quot;&quot;subflow&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;Invoice&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;Based on the shipped or on the ordered quantities.&quot;&quot;&quot;" name="note"/>
<field name="process_id" ref="process_process_salesprocess0"/>
<field name="subflow_id" ref="account.process_process_invoiceprocess0"/>
<field eval="&quot;&quot;&quot;object.state=='draft'&quot;&quot;&quot;" name="model_states"/>
<field eval="0" name="flow_start"/>
</record>
<!--
Process Condition
-->
<record id="process_condition_conditionafterdelivery0" model="process.condition">
<field name="model_id" ref="sale.model_sale_order"/>
<field name="node_id" ref="process_node_invoiceafterdelivery0"/>
<field eval="&quot;&quot;&quot;object.order_policy=='postpaid'&quot;&quot;&quot;" name="model_states"/>
<field eval="&quot;&quot;&quot;condition_after_delivery&quot;&quot;&quot;" name="name"/>
</record>
<!--
Process Transition
-->
@ -142,29 +75,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="&quot;&quot;&quot;Create Procurement Order&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;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. &quot;&quot;&quot;" 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="&quot;&quot;&quot;Create Pick List&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;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.&quot;&quot;&quot;" 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="&quot;&quot;&quot;Create Delivery Order&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;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.&quot;&quot;&quot;" 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"/>
@ -175,21 +85,6 @@
<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="&quot;&quot;&quot;Create Invoice&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;The invoice is created automatically if the shipping policy is 'Invoice from pick' or 'Invoice on order after delivery'.&quot;&quot;&quot;" 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="&quot;&quot;&quot;Procurement of sold material&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;For every sales order line, a procurement order is created to supply the sold product.&quot;&quot;&quot;" 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"/>
@ -216,49 +111,7 @@
<field eval="&quot;&quot;&quot;Cancel&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_confirmquotation0"/>
</record>
<record id="process_transition_action_assign0" model="process.transition.action">
<field eval="&quot;&quot;&quot;action_assign&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Assign&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_packing0"/>
</record>
<record id="process_transition_action_forceassignation0" model="process.transition.action">
<field eval="&quot;&quot;&quot;force_assign&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Force Assignation&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_packing0"/>
</record>
<record id="process_transition_action_cancel1" model="process.transition.action">
<field eval="&quot;&quot;&quot;action_cancel&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Cancel&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_packing0"/>
</record>
<record id="process_transition_action_cancelassignation0" model="process.transition.action">
<field eval="&quot;&quot;&quot;cancel_assign&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Cancel Assignation&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_deliver0"/>
</record>
<record id="process_transition_action_validate0" model="process.transition.action">
<field eval="&quot;&quot;&quot;test_finished&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Validate&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_deliver0"/>
</record>
<record id="process_transition_action_cancel2" model="process.transition.action">
<field eval="&quot;&quot;&quot;action_cancel&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Cancel&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_deliver0"/>
</record>
<record id="process_transition_action_createinvoice0" model="process.transition.action">
<field eval="&quot;&quot;&quot;action_invoice_create&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>

View File

@ -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')
@ -63,6 +60,7 @@ class sale_report(osv.osv):
'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
}
_order = 'date desc'
def init(self, cr):
tools.drop_view_if_exists(cr, 'sale_report')
cr.execute("""
@ -86,8 +84,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 +106,6 @@ class sale_report(osv.osv):
s.shop_id,
s.company_id,
s.state,
s.shipped,
s.pricelist_id,
s.project_id
)

View File

@ -20,7 +20,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"/>

View File

@ -27,17 +27,9 @@ class sale_configuration(osv.osv_memory):
_inherit = 'sale.config.settings'
_columns = {
'group_invoice_so_lines': fields.boolean('Generate invoices based on the sale order',
'group_invoice_so_lines': fields.boolean('Generate invoices based on the sale order lines',
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('Generate invoices after and 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("Prepare invoices based on task's 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('Prepare invoices based on timesheets',
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.
@ -48,17 +40,7 @@ class sale_configuration(osv.osv_memory):
(650/day for a developer), the duration (one year support contract).
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')],
'The default invoicing method is', default_model='sale.order',
help="You can generate invoices based on sales orders or based on shippings."),
'module_delivery': fields.boolean('Allow adding 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."""),
'time_unit': fields.many2one('product.uom', 'The default working time unit for services is'),
'default_picking_policy' : fields.boolean("Deliver all at once when all products are available.",
help = "Sales order by default will be configured to deliver all products at once instead of delivering each product when it is available. This may have an impact on the shipping price."),
'group_sale_pricelist':fields.boolean("Use pricelists to adapt your price per customers",
implied_group='product.group_sale_pricelist',
help="""Allows to manage different prices based on rules per category of customers.
@ -69,15 +51,9 @@ class sale_configuration(osv.osv_memory):
'group_sale_delivery_address': fields.boolean("Allow a different address for delivery and invoicing ",
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('Product properties on order lines',
implied_group='sale.group_mrp_properties',
help="Allows you to tag sale order lines with properties."),
'group_discount_per_so_line': fields.boolean("Allow setting a discount on the sale order lines",
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("Allow configuring alerts by customer or products",
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,16 +71,15 @@ 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"),
'module_sale_stock': fields.boolean("Sale and Warehouse Management",
help="""Allows you to Make Quotation, Sale Order using different Order policy and Manage Related Stock.
This installs the module sale_stock."""),
}
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
@ -113,31 +88,18 @@ 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
_defaults = {
'default_order_policy': 'manual',
'time_unit': _get_default_time_unit,
}
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_product_consultant')
product.write({'uom_id': wizard.time_unit.id, 'uom_po_id': wizard.time_unit.id})
@ -145,14 +107,7 @@ class sale_configuration(osv.osv_memory):
if wizard.module_project and wizard.time_unit:
user = self.pool.get('res.users').browse(cr, uid, uid, context)
user.company_id.write({'project_time_mode_id': wizard.time_unit.id})
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:
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):
@ -167,8 +122,6 @@ class sale_configuration(osv.osv_memory):
'module_account_analytic_analysis': timesheet,
}}
class account_config_settings(osv.osv_memory):
_inherit = 'account.config.settings'
_columns = {

View File

@ -16,39 +16,21 @@
<field name="group_invoice_so_lines" on_change="onchange_invoice_methods(group_invoice_so_lines, group_invoice_deli_orders)" class="oe_inline"/>
<label for="group_invoice_so_lines"/>
</div>
<div>
<field name="group_invoice_deli_orders" class="oe_inline" on_change="onchange_invoice_methods(group_invoice_so_lines, group_invoice_deli_orders)"/>
<label for="group_invoice_deli_orders"/>
</div>
<div>
<field name="task_work" class="oe_inline" on_change="onchange_task_work(task_work)"/>
<label for="task_work"/>
</div>
<div>
<div name="timesheet">
<field name="timesheet" class="oe_inline" on_change="onchange_timesheet(timesheet)"/>
<label for="timesheet"/>
</div>
</div>
</group>
<group>
<label for="id" string="Default Options" attrs="{'invisible':['|',('group_invoice_so_lines','=',False),('group_invoice_deli_orders','=',False),('group_invoice_so_lines','=',False), ('group_invoice_deli_orders','=',False),('task_work','=',False), ('module_account_analytic_analysis','=',False)]}"/>
<group name='default_options'>
<label for="id" string="Default Options" attrs="{'invisible':[('module_account_analytic_analysis','=',False)]}"/>
<div>
<div attrs="{'invisible':['|',('group_invoice_so_lines','=',False),('group_invoice_deli_orders','=',False)],'required': ['|',('group_invoice_so_lines','=',True),('group_invoice_deli_orders','=',True)]}">
<label for="default_order_policy"/>
<field name="default_order_policy" class="oe_inline"/>
</div>
<div attrs="{'invisible':[('group_invoice_so_lines','=',False), ('group_invoice_deli_orders','=',False)]}">
<field name="module_delivery" class="oe_inline"/>
<label for="module_delivery"/>
</div>
<div attrs="{'invisible': [('task_work','=',False), ('module_account_analytic_analysis','=',False)],'required': ['|', ('task_work','=',True), ('module_account_analytic_analysis','=',True)]}">
<div name='time_unit' attrs="{'invisible': [('module_account_analytic_analysis','=',False)],'required':[('module_account_analytic_analysis','=',True)]}">
<label for="time_unit"/>
<field name="time_unit" domain="[('category_id.name','=','Working Time')]" class="oe_inline"/>
</div>
</div>
</group>
<field name="module_project_timesheet" invisible="1"/>
<field name="module_project_mrp" invisible="1"/>
</div>
<div name="Customer Features" position="inside">
<div>
@ -67,16 +49,12 @@
<group name="Sale" position="before">
<group>
<label for="id" string="Warehouse Features"/>
<div>
<div attrs="{'invisible':[('group_invoice_deli_orders','=',False)]}">
<field name="default_picking_policy" class="oe_inline"/>
<label for="default_picking_policy"/>
<div name="warehouse_features">
<div name='module_sale_stock'>
<field name="module_sale_stock" class="oe_inline"/>
<label for="module_sale_stock"/>
</div>
<div>
<field name="group_mrp_properties" class="oe_inline"/>
<label for="group_mrp_properties"/>
</div>
<div>
<div name='module_sale_journal'>
<field name="module_sale_journal" class="oe_inline"/>
<label for="module_sale_journal"/>
</div>
@ -99,10 +77,6 @@
</group>
</group>
<div name="Sale Features" position="inside">
<div>
<field name="group_multiple_shops" class="oe_inline"/>
<label for="group_multiple_shops"/>
</div>
<div name="module_sale_margin">
<field name="module_sale_margin" class="oe_inline"/>
<label for="module_sale_margin"/>

View File

@ -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),
@ -51,15 +50,12 @@ class sale_order(osv.osv):
_inherit = ['mail.thread', 'ir.needaction_mixin']
_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 = {}
@ -217,10 +175,9 @@ class sale_order(osv.osv):
('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 processing of a document linked to the sale order. \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."),
@ -228,28 +185,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."),
@ -276,13 +221,12 @@ 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).", 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',
'state': 'draft',
@ -305,35 +249,10 @@ 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 sales order, you must cancel it.\nTo do so, you must first cancel related picking for 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):
v = {}
if shop_id:
shop = self.pool.get('sale.shop').browse(cr, uid, shop_id)
v['project_id'] = shop.project_id.id
# Que faire si le client a une pricelist a lui ?
if shop.pricelist_id.id:
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):
return {}
@ -343,15 +262,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:
@ -374,30 +284,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)
@ -430,7 +317,6 @@ class sale_order(osv.osv):
if not journal_ids:
raise osv.except_osv(_('Error!'),
_('Please define sales journal for this company: "%s" (id:%d).') % (order.company_id.name, order.company_id.id))
invoice_vals = {
'name': order.client_order_ref or '',
'origin': order.name,
@ -451,7 +337,6 @@ class sale_order(osv.osv):
# Care for deprecated _inv_get() hook - FIXME: to be removed after 6.1
invoice_vals.update(self._inv_get(cr, uid, order, context=context))
return invoice_vals
def _make_invoice(self, cr, uid, order, lines, context=None):
@ -546,29 +431,6 @@ class sale_order(osv.osv):
result['res_id'] = inv_ids and inv_ids[0] 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')
act_obj = self.pool.get('ir.actions.act_window')
result = mod_obj.get_object_reference(cr, uid, 'stock', 'action_picking_tree')
id = result and result[1] or False
result = act_obj.read(cr, uid, [id], context=context)[0]
#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:
result['domain'] = "[('id','in',["+','.join(map(str, pick_ids))+"])]"
else:
res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_form')
result['views'] = [(res and res[1] or False, 'form')]
result['res_id'] = pick_ids and pick_ids[0] or False
return result
def test_no_product(self, cr, uid, order, context):
for line in order.order_line:
if line.product_id and (line.product_id.type<>'service'):
@ -579,7 +441,6 @@ class sale_order(osv.osv):
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 = {}
@ -618,8 +479,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:
@ -627,8 +486,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)
@ -683,6 +540,7 @@ class sale_order(osv.osv):
#
if order.state == 'invoice_except':
self.write(cr, uid, [order.id], {'state': 'progress'}, context=context)
self.invoice_paid_send_note(cr, uid, [order.id], context=context)
return True
def action_cancel(self, cr, uid, ids, context=None):
@ -690,22 +548,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(
_('Cannot cancel sales order!'),
_('You must first cancel all delivery order(s) 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(
@ -745,8 +588,6 @@ class sale_order(osv.osv):
if not o.order_line:
raise osv.except_osv(_('Error!'),_('You cannot confirm a sale order which has no line.'))
noprod = self.test_no_product(cr, uid, o, context)
if noprod and o.order_policy=='picking':
self.write(cr, uid, [o.id], {'order_policy': 'manual'}, context=context)
if (o.order_policy == 'manual') or noprod:
self.write(cr, uid, [o.id], {'state': 'manual', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})
else:
@ -785,240 +626,9 @@ 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:])
}
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',
'type':'out',
#'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
def action_done(self, cr, uid, ids, context=None):
self.done_send_note(cr, uid, ids, context=context)
return self.write(cr, uid, ids, {'state': 'done'}, context=context)
# ------------------------------------------------
# OpenChatter methods and notifications
@ -1038,18 +648,10 @@ class sale_order(osv.osv):
def cancel_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
self.message_post(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_post(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_post(cr, uid, ids, body=_("Order <b>delivered</b>."), context=context)
def done_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
self.message_post(cr, uid, [obj.id], body=_("Sale Order for <em>%s</em> set to <b>Done</b>") % (obj.partner_id.name), context=context)
def invoice_paid_send_note(self, cr, uid, ids, context=None):
self.message_post(cr, uid, ids, body=_("Invoice <b>paid</b>."), context=context)
@ -1059,10 +661,6 @@ class sale_order(osv.osv):
for invoice in (inv for inv in order.invoice_ids if inv.id == invoice_id):
self.message_post(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_post(cr, uid, ids, body=_('Sale order set to draft.'), context=context)
sale_order()
# TODO add a field price_unit_uos
@ -1083,15 +681,6 @@ 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:
proxy = self.pool.get('ir.model.data')
@ -1106,26 +695,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('Product Price'), readonly=True, states={'draft': [('readonly', False)]}),
'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute= dp.get_precision('Account')),
'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_compute= dp.get_precision('Discount'), 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. \
@ -1141,17 +722,26 @@ 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,
}
def _get_line_qty(self, cr, uid, line, context=None):
if (line.order_id.invoice_quantity=='order'):
if line.product_uos:
return line.product_uos_qty or 0.0
return line.product_uom_qty
def _get_line_uom(self, cr, uid, line, context=None):
if (line.order_id.invoice_quantity=='order'):
if line.product_uos:
return line.product_uos.id
return line.product_uom.id
def _prepare_order_line_invoice_line(self, cr, uid, line, account_id=False, context=None):
"""Prepare the dict of values to create the new invoice line for a
sale order line. This method may be overridden to implement custom
@ -1163,25 +753,7 @@ class sale_order_line(osv.osv):
(this is used for returning products including service)
:return: dict of values to create() the invoice line
"""
def _get_line_qty(line):
if (line.order_id.invoice_quantity=='order') or not line.procurement_id:
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.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)
res = {}
if not line.invoiced:
if not account_id:
if line.product_id:
@ -1197,8 +769,8 @@ class sale_order_line(osv.osv):
'property_account_income_categ', 'product.category',
context=context)
account_id = prop and prop.id or False
uosqty = _get_line_qty(line)
uos_id = _get_line_uom(line)
uosqty = self._get_line_qty(cr, uid, line, context=context)
uos_id = self._get_line_uom(cr, uid, line, context=context)
pu = 0.0
if uosqty:
pu = round(line.price_unit * line.product_uom_qty / uosqty,
@ -1208,7 +780,7 @@ class sale_order_line(osv.osv):
if not account_id:
raise osv.except_osv(_('Error!'),
_('There is no Fiscal Position defined or Income category account defined for default properties of Product categories.'))
return {
res = {
'name': line.name,
'origin': line.order_id.name,
'account_id': account_id,
@ -1221,7 +793,7 @@ class sale_order_line(osv.osv):
'account_analytic_id': line.order_id.project_id and line.order_id.project_id.id or False,
}
return False
return res
def invoice_line_create(self, cr, uid, ids, context=None):
if context is None:
@ -1247,11 +819,6 @@ class sale_order_line(osv.osv):
for line in self.browse(cr, uid, ids, context=context):
if line.invoiced:
raise osv.except_osv(_('Invalid Action!'), _('You cannot cancel a sale order line that has already been invoiced.'))
for move_line in line.move_ids:
if move_line.state != 'cancel':
raise osv.except_osv(
_('Cannot cancel sales order line!'),
_('You must first cancel stock moves attached to this sales order line.'))
return self.write(cr, uid, ids, {'state': 'cancel'})
def button_confirm(self, cr, uid, ids, context=None):
@ -1287,56 +854,9 @@ class sale_order_line(osv.osv):
def copy_data(self, cr, uid, id, default=None, context=None):
if not default:
default = {}
default.update({'state': 'draft', 'move_ids': [], 'invoiced': False, 'invoice_lines': []})
default.update({'state': 'draft', '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,
lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
@ -1354,15 +874,14 @@ class sale_order_line(osv.osv):
context_partner = {'lang': lang, 'partner_id': partner_id}
if not product:
return {'value': {'th_weight': 0, 'product_packaging': False,
return {'value': {'th_weight': 0,
'product_uos_qty': qty}, 'domain': {'product_uom': [],
'product_uos': []}}
if not date_order:
date_order = time.strftime(DEFAULT_SERVER_DATE_FORMAT)
res = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging, context=context)
result = res.get('value', {})
warning_msgs = res.get('warning') and res['warning']['message'] or ''
#
result = {}
warning_msgs = {}
product_obj = product_obj.browse(cr, uid, product, context=context)
uom2 = False
@ -1379,7 +898,6 @@ class sale_order_line(osv.osv):
uos = False
fpos = fiscal_position and self.pool.get('account.fiscal.position').browse(cr, uid, fiscal_position) or False
if update_tax: #The quantity only have changed
result['delay'] = (product_obj.sale_delay or 0.0)
result['tax_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, product_obj.taxes_id)
result.update({'type': product_obj.procure_method})
@ -1420,14 +938,6 @@ class sale_order_line(osv.osv):
if not uom2:
uom2 = product_obj.uom_id
compare_qty = float_compare(product_obj.virtual_available * uom2.factor, qty * product_obj.uom_id.factor, precision_rounding=product_obj.uom_id.rounding)
if (product_obj.type=='product') and int(compare_qty) == -1 \
and (product_obj.procure_method=='make_to_stock'):
warn_msg = _('You plan to sell %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') % \
(qty, uom2 and uom2.name or product_obj.uom_id.name,
max(0,product_obj.virtual_available), product_obj.uom_id.name,
max(0,product_obj.qty_available), product_obj.uom_id.name)
warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n"
# get unit price
if not pricelist:

View File

@ -22,7 +22,6 @@
<!-- Resource: sale.shop -->
<record id="sale_shop_1" 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"/>

View File

@ -9,7 +9,6 @@
<field name="shop_id" ref="sale_shop_1"/>
<field name="user_id" ref="base.user_demo"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="order_policy">prepaid</field>
</record>
<record id="sale_order_line_1" model="sale.order.line">
@ -60,7 +59,6 @@
<field name="product_uos_qty">24</field>
<field name="product_uom" ref="product.product_uom_hour"/>
<field name="price_unit">75.00</field>
<field name="type">make_to_order</field>
</record>
<record id="sale_order_line_5" model="sale.order.line">
@ -71,7 +69,6 @@
<field name="product_uos_qty">30</field>
<field name="product_uom" ref="product.product_uom_hour"/>
<field name="price_unit">38.25</field>
<field name="type">make_to_order</field>
</record>
<record id="sale_order_3" model="sale.order">
@ -92,7 +89,6 @@
<field name="product_uos_qty">10</field>
<field name="product_uom" ref="product.product_uom_hour"/>
<field name="price_unit">30.75</field>
<field name="type">make_to_order</field>
</record>
<record id="sale_order_line_7" model="sale.order.line">
@ -112,7 +108,6 @@
<field name="shop_id" ref="sale_shop_1"/>
<field name="user_id" ref="base.user_root"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="order_policy">prepaid</field>
</record>
<record id="sale_order_line_8" model="sale.order.line">
@ -123,7 +118,6 @@
<field name="product_uos_qty">16</field>
<field name="product_uom" ref="product.product_uom_hour"/>
<field name="price_unit">75.00</field>
<field name="type">make_to_order</field>
</record>
<record id="sale_order_line_9" model="sale.order.line">
@ -163,7 +157,6 @@
<field name="shop_id" ref="sale_shop_1"/>
<field name="user_id" ref="base.user_demo"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="order_policy">picking</field>
</record>
<record id="sale_order_line_12" model="sale.order.line">
@ -203,7 +196,6 @@
<field name="shop_id" ref="sale_shop_1"/>
<field name="user_id" ref="base.user_root"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="order_policy">picking</field>
</record>
<record id="sale_order_line_15" model="sale.order.line">
@ -220,7 +212,6 @@
<field name="partner_id" ref="base.res_partner_11"/>
<field name="partner_invoice_id" ref="base.res_partner_address_20"/>
<field name="partner_shipping_id" ref="base.res_partner_address_20"/>
<field name="shop_id" ref="sale_shop_1"/>
<field name="user_id" ref="base.user_root"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="order_policy">manual</field>
@ -297,8 +288,6 @@
</record>
<!-- Confirm some Sale Orders-->
<workflow action="order_confirm" model="sale.order" ref="sale_order_1"/>
<workflow action="order_confirm" model="sale.order" ref="sale_order_5"/>
<workflow action="order_confirm" model="sale.order" ref="sale_order_7"/>
<record id="message_sale_1" model="mail.message">
@ -352,9 +341,5 @@ Sales Department</field>
<field eval="[(4, ref('base.group_sale_salesman'))]" name="groups_id"/>
</record>
<!-- Run all schedulers -->
<function model="procurement.order" name="run_scheduler"/>
</data>
</openerp>

View File

@ -21,9 +21,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"/>
@ -34,15 +32,15 @@
</group>
</group>
</form>
</field>
</field>
</record>
<record id="view_shop_tree" model="ir.ui.view">
<field name="name">sale.shop</field>
<field name="model">sale.shop</field>
<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>
@ -67,8 +65,6 @@
</field>
</record>
<menuitem action="action_shop_form" id="menu_action_shop_form" parent="base.menu_base_config" sequence="35"
groups="stock.group_locations"/>
<!-- Sale order Read/Unread actions -->
<record id="actions_server_sale_order_unread" model="ir.actions.server">
@ -154,8 +150,6 @@
<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" class="oe_highlight"/>
<button name="action_quotation_send" string="Send by Mail" type="object" states="sent"/>
<button name="print_quotation" string="Print" type="object" states="draft" class="oe_highlight"/>
@ -164,16 +158,13 @@
<button name="action_button_confirm" states="sent" string="Confirm" class="oe_highlight" type="object"/>
<button name="action_view_invoice" string="View Invoice" type="object" class="oe_highlight"
attrs="{'invisible': [('invoice_exists', '=', False)]}"/>
<button name="action_view_delivery" string="View 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"/>
<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,invoiced,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}'/>
</header>
<sheet>
<h1>
@ -190,7 +181,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" invisible="1"/>
<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>
@ -209,14 +200,15 @@
<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)"/>
<field name="product_uom" groups="product.group_uom" options='{"no_open": true}' 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, 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>
<label for="product_uos_qty" groups="product.group_uos"/>
@ -231,22 +223,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','&lt;&gt;','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"/>
@ -254,22 +235,17 @@
<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="sequence" widget="handle"/>
<field name="state" invisible="1"/>
<field name="delay" 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"
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)"/>
<field name="name"/>
<field name="type" invisible="1"/>
<field name="product_uom_qty"
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}"
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)"/>
@ -302,13 +278,7 @@
</page>
<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="invoice_quantity" invisible="True"/>
</group>
<group groups="base.group_user">
<group name="sales_person" groups="base.group_user">
<field name="user_id"/>
<field groups="base.group_no_one" name="origin"/>
</group>
@ -319,7 +289,6 @@
</group>
<group>
<field name="invoiced"/>
<field name="shipped"/>
<field name="invoice_exists" invisible="1"/>
</group>
</group>
@ -337,16 +306,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 &lt; 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>
@ -515,9 +474,9 @@
<form string="Sales Order Lines" version="7.0">
<header>
<span groups="base.group_user">
<button name="button_cancel" string="Cancel" type="object" icon="gtk-cancel" states="confirmed,exception"/>
<button name="%(action_view_sale_order_line_make_invoice)d" string="Create Invoice" type="action" states="done" icon="gtk-go-forward" attrs="{'invisible': [('invoiced', '=', 1)]}" class="oe_highlight"/>
<button name="button_done" string="Done" type="object" states="confirmed,exception" icon="gtk-go-forward" class="oe_highlight"/>
<button name="button_cancel" string="Cancel" type="object" states="confirmed,exception"/>
<button name="%(action_view_sale_order_line_make_invoice)d" string="Create Invoice" type="action" attrs="{'invisible': ['|',('invoiced', '=', 1), ('state', 'not in', ('confirmed', 'draft'))]}" class="oe_highlight"/>
<button name="button_done" string="Done" type="object" attrs="{'invisible': ['|',('invoiced', '=', 0), ('state', 'not in', ('confirmed', 'exception'))]}" class="oe_highlight"/>
</span>
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,done" statusbar_colors='{"exception":"red","cancel":"red"}'/>
</header>

View File

@ -13,12 +13,14 @@
<field name="flow_start">True</field>
<field name="name">draft</field>
</record>
<record id="act_sent" model="workflow.activity">
<field name="wkf_id" ref="wkf_sale"/>
<field name="name">sent</field>
<field name="kind">function</field>
<field name="action">write({'state':'sent'})</field>
</record>
</record>
<record id="act_router" model="workflow.activity">
<field name="wkf_id" ref="wkf_sale"/>
<field name="name">router</field>
@ -26,23 +28,21 @@
<field name="action">action_wait()</field>
<field name="split_mode">OR</field>
</record>
<record id="act_wait_invoice" model="workflow.activity">
<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"/>
<field name="name">done</field>
<field name="flow_stop">True</field>
<field name="kind">function</field>
<field name="action">write({'state':'done'})</field>
<field name="action">action_done()</field>
<field name="join_mode">AND</field>
</record>
<record id="act_cancel" model="workflow.activity">
<field name="wkf_id" ref="wkf_sale"/>
<field name="name">cancel</field>
@ -50,6 +50,7 @@
<field name="kind">stopall</field>
<field name="action">action_cancel()</field>
</record>
<record id="act_cancel2" model="workflow.activity">
<field name="wkf_id" ref="wkf_sale"/>
<field name="name">cancel2</field>
@ -57,13 +58,7 @@
<field name="kind">stopall</field>
<field name="action">action_cancel()</field>
</record>
<record id="act_cancel3" model="workflow.activity">
<field name="wkf_id" ref="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_invoice" model="workflow.activity">
<field name="wkf_id" ref="wkf_sale"/>
<field name="name">invoice</field>
@ -90,121 +85,53 @@
<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 -->
<record id="trans_invoice_end_done" model="workflow.transition">
<field name="act_from" ref="act_invoice_end"/>
<field name="act_to" ref="act_done"/>
</record>
<record id="trans_draft_router" model="workflow.transition">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_router"/>
<field name="signal">order_confirm</field>
</record>
<record id="trans_draft_sent" model="workflow.transition">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_sent"/>
<field name="signal">quotation_sent</field>
</record>
<record id="trans_sent_cancel" model="workflow.transition">
<field name="act_from" ref="act_sent"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">cancel</field>
</record>
<record id="trans_sent_router" model="workflow.transition">
<field name="act_from" ref="act_sent"/>
<record id="trans_draft_router" model="workflow.transition">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_router"/>
<field name="signal">order_confirm</field>
</record>
<record id="trans_draft_cancel" model="workflow.transition">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">cancel</field>
</record>
<record id="trans_sent_router" model="workflow.transition">
<field name="act_from" ref="act_sent"/>
<field name="act_to" ref="act_router"/>
<field name="signal">order_confirm</field>
</record>
<record id="trans_sent_cancel" model="workflow.transition">
<field name="act_from" ref="act_sent"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">cancel</field>
</record>
<record id="trans_router_wait_invoice" model="workflow.transition">
<field name="act_from" ref="act_router"/>
<field name="act_to" ref="act_wait_invoice"/>
</record>
<record id="trans_wait_invoice_all_lines_invoiced" model="workflow.transition">
<field name="act_from" ref="act_wait_invoice"/>
<field name="act_to" ref="act_invoice_end"/>
<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"/>
<field name="condition">(order_policy=='picking')</field>
</record>
<record id="trans_router_wait_invoice" model="workflow.transition">
<field name="act_from" ref="act_router"/>
<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"/>
<field name="act_to" ref="act_cancel2"/>
<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"/>
<field name="act_to" ref="act_invoice"/>
<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"/>
@ -230,38 +157,10 @@
<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"/>
<record id="trans_invoice_end_done" model="workflow.transition">
<field name="act_from" ref="act_invoice_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>

View File

@ -1,4 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_shop,sale.shop,model_sale_shop,base.group_user,1,0,0,0
access_sale_order,sale.order,model_sale_order,base.group_sale_salesman,1,1,1,0
access_sale_order_line,sale.order.line,model_sale_order_line,base.group_sale_salesman,1,1,1,1
@ -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,16 @@ 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 +35,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 +44,4 @@ 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
access_account_move_line_salesman,account.move.line invoice,account.model_account_move_line,base.group_sale_salesman,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sale_shop sale.shop model_sale_shop base.group_user 1 0 0 0
3 access_sale_order sale.order model_sale_order base.group_sale_salesman 1 1 1 0
4 access_sale_order_line sale.order.line model_sale_order_line base.group_sale_salesman 1 1 1 1
9 access_account_invoice_line_salesman account_invoice.line salesman account.model_account_invoice_line base.group_sale_salesman 1 1 1 0
10 access_account_payment_term_salesman account_payment_term salesman account.model_account_payment_term base.group_sale_salesman 1 0 0 0
11 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
12 access_sale_order_manager sale.order.manager model_sale_order base.group_sale_manager 1 1 1 1
13 access_sale_order_accountant sale.order.accountant model_sale_order account.group_account_user 1 1 0 0
14 access_sale_report_salesman sale.report model_sale_report base.group_sale_salesman 1 1 1 0
17 access_sale_account_journal account.journal sale order.user account.model_account_journal base.group_sale_salesman 1 0 0 0
18 access_res_partner_sale_user res.partner.sale.user base.model_res_partner base.group_sale_salesman 1 0 0 0
19 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
20 access_product_template_sale_user product.template sale use product.model_product_template base.group_sale_salesman 1 0 0 0
21 access_product_product_sale_user product.product sale use product.model_product_product base.group_sale_salesman 1 0 0 0
22 access_sale_shop_manager account.journal sale order.user model_sale_shop base.group_sale_manager 1 1 1 1
23 access_account_fiscalyear_user account.fiscalyear.user account.model_account_fiscalyear base.group_sale_salesman 1 0 0 0
24 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
25 access_ir_attachment_sales ir.attachment.sales base.model_ir_attachment base.group_sale_salesman 1 1 1 0
26 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
27 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
28 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
29 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
30 access_product_pricelist_sale_user product.pricelist.sale.user product.model_product_pricelist base.group_sale_salesman 1 0 0 0
31 access_account_account_salesman account_account salesman account.model_account_account base.group_sale_salesman 1 0 0 0
32 access_sale_shop_sale_user sale.shop.sale.user model_sale_shop base.group_sale_salesman 1 0 0 0
35 access_product_uom_sale_manager product.uom salemanager product.model_product_uom base.group_sale_manager 1 1 1 1
36 access_product_ul_sale_manager product.ul salemanager product.model_product_ul base.group_sale_manager 1 1 1 1
37 access_product_category_sale_manager product.category salemanager product.model_product_category base.group_sale_manager 1 1 1 1
38 access_product_packaging_sale_manager access_product_supplierinfo_user product.packaging salemanager product.supplierinfo.user product.model_product_packaging product.model_product_supplierinfo base.group_sale_manager base.group_sale_salesman 1 1 0 1 0 1 0
39 access_product_supplierinfo_sale_manager product.supplierinfo salemanager product.model_product_supplierinfo base.group_sale_manager 1 1 1 1
40 access_pricelist_partnerinfo_sale_manager pricelist.partnerinfo salemanager product.model_pricelist_partnerinfo base.group_sale_manager 1 1 1 1
41 access_product_price_type_sale_manager product.price.type salemanager product.model_product_price_type base.group_sale_manager 1 1 1 1
44 access_product_group_res_partner_sale_manager res_partner group_sale_manager base.model_res_partner base.group_sale_manager 1 1 1 0
45 access_product_pricelist_version_sale_manager product.pricelist.version sale_manager product.model_product_pricelist_version base.group_sale_manager 1 1 1 1
46 access_account_invoice_report_salesman account.invoice.report salesman account.model_account_invoice_report base.group_sale_salesman 1 0 0 0
47 access_stock_warehouse_orderpoint_sale_salesman access_account_move_line_salesman stock.warehouse.orderpoint account.move.line invoice procurement.model_stock_warehouse_orderpoint account.model_account_move_line 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

View File

@ -29,11 +29,6 @@
<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"/>

View File

@ -1,88 +1,79 @@
-
In order to test the cancel sale order.
-
Now I cancel Quotation.
-
!python {model: sale.order}: |
self.action_cancel(cr, uid, [ref("sale_order_8")])
-
I check state of Quotation after cancelled.
-
!assert {model: sale.order, id: sale_order_8, 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("sale_order_8")])
-
I confirm order.
I confirm order (with at least 2 lines)
-
!workflow {model: sale.order, action: order_confirm, ref: sale_order_8}
-
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("sale_order_8"))])
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': 2, '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("sale_order_8"))])
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: sale_order_8, string: Sale order should be in shipping exception}:
- state == "shipping_except"
-
Now I regenerate shipment.
-
!workflow {model: sale.order, action: ship_recreate, ref: sale_order_8}
-
I check state of order in 'To Invoice'.
I check state of order in 'Sale Order'.
-
!assert {model: sale.order, id: sale_order_8, string: Sale order should be In Progress state}:
- state == 'manual'
- state == 'manual'
-
I make invoice for order.
I check that Invoice should not be created.
-
!workflow {model: sale.order, action: manual_invoice, ref: sale_order_8}
!python {model: sale.order}: |
sale_order = self.browse(cr, uid, ref("sale_order_8"))
assert len(sale_order.invoice_ids) == False, "Invoice should not be created."
-
To cancel the sale order from Invoice Exception, I have to cancel the invoice of sale order.
I create an invoice for the first line
-
!python {model: sale.order.line.make.invoice}: |
ctx = context.copy()
ctx.update({"active_model": 'sale.order.line', "active_ids": [ref("sale_order_line_20")], "active_id":ref("sale_order_line_20")})
pay_id = self.create(cr, uid, {})
self.make_invoices(cr, uid, [pay_id], context=ctx)
invoice_ids = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_8")).invoice_ids
-
I create an invoice for a fixed price (25% of the second line, thus 5)
-
!python {model: sale.advance.payment.inv}: |
ctx = context.copy()
ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale_order_8")], "active_id":ref("sale_order_8")})
pay_id = self.create(cr, uid, {'advance_payment_method': 'fixed', 'amount': 5})
self.create_invoices(cr, uid, [pay_id], context=ctx)
invoice_ids = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_8")).invoice_ids
-
I create an invoice for the remaining and check the amount (should be the remaining amount of second line)
-
!python {model: sale.advance.payment.inv}: |
ctx = context.copy()
ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale_order_8")], "active_id":ref("sale_order_8")})
pay_id = self.create(cr, uid, {'advance_payment_method': 'all'})
self.create_invoices(cr, uid, [pay_id], context=ctx)
invoice_ids = self.pool.get('sale.order').browse(cr, uid, ref("sale_order_8")).invoice_ids
assert len(invoice_ids) == 3, "All invoices are not created"
for invoice in invoice_ids:
assert invoice.amount_total in (7290,5,20), "Invoice total is not correct"
-
I cancel all the invoices.
-
!python {model: sale.order}: |
import netsvc
invoice_ids = self.browse(cr, uid, ref("sale_order_8")).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)
for invoice in invoice_ids:
wf_service.trg_validate(uid, 'account.invoice', invoice.id, 'invoice_cancel', cr)
-
I check order status in "Invoice Exception" and related invoice is in cancel state.
-
!assert {model: sale.order, id: sale_order_8, string: Sale order should be in Invoice Exception state}:
- state == "invoice_except", "Order should be in Invoice Exception state after cancel Invoice"
-
I click recreate invoice.
-
Then I click on the Ignore Exception button.
!workflow {model: sale.order, action: invoice_recreate, ref: sale_order_8}
-
I check that the invoice is correctly created with all lines.
-
!workflow {model: sale.order, action: invoice_corrected, ref: sale_order_8}
!python {model: sale.order}: |
sale_order = self.browse(cr, uid, ref("sale_order_8"))
total_order_line = 0
assert len(sale_order.invoice_ids), "Invoice should be created."
for invoice in sale_order.invoice_ids:
if invoice.state != 'cancel':
total_order_line += len(invoice.invoice_line)
assert total_order_line == 2, "wrong number of invoice lines"
-
I check state of order in 'In Progress'.
-
!assert {model: sale.order, id: sale_order_8, string: Sale order should be In progress state}:
- state == 'progress'

View File

@ -3,12 +3,12 @@
-
!python {model: sale.order}: |
try:
self.unlink(cr, uid, [ref("sale_order_1")])
self.unlink(cr, uid, [ref("sale_order_7")])
except Exception,e:
pass
-
I make duplicate order and delete.
-
!python {model: sale.order}: |
id = self.copy(cr, uid, ref('sale_order_1'))
id = self.copy(cr, uid, ref('sale_order_7'))
self.unlink(cr, uid, [id])

View File

@ -73,7 +73,6 @@
"__import_module": "sale",
"__import_model": "sale.order.line",
"name": "PC Assemble SC234",
"date_planned": "2011-09-30",
"price_unit": 150.0,
"product_id": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_product_3", "[PCSC234] PC Assemble SC234"],
"product_qty": 1.0,
@ -86,7 +85,6 @@
"__import_module": "sale",
"__import_model": "sale.order.line",
"name": "PC on Demand",
"date_planned": "2011-09-15",
"price_unit": 100.0,
"product_id": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_product_5", "[PC-DEM] PC on Demand"],
"product_qty": 2.0,
@ -108,12 +106,10 @@
assert len(order_new.order_line) == 2, "Sale order lines mismatch"
for sale_line in order_new.order_line:
if sale_line.name == 'PC Assemble SC234':
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 == 'PC on Demand':
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"

View File

@ -67,13 +67,4 @@
assert sale_order.invoice_ids, "Invoice should be created."
assert sale_order.invoice_exists, "Order is not invoiced."
assert sale_order.invoiced, "Order is not paid."
assert sale_order.state == 'progress', 'Order should be in Progress.'
-
I set order policy "Deliver & invoice on demand" as default policy.
-
!record {model: sale.config.settings, id: sale_configuration_0}:
default_order_policy: 'manual'
-
!python {model: sale.config.settings}: |
self.execute(cr, uid, [ref("sale_configuration_0")], context=context)
assert sale_order.state == 'done', 'Order should be Done.'

View File

@ -104,7 +104,7 @@ class sale_order_line_make_invoice(osv.osv_memory):
flag = False
break
if flag:
wf_service.trg_validate(uid, 'sale.order', line.order_id.id, 'all_lines', cr)
wf_service.trg_validate(uid, 'sale.order', line.order_id.id, 'manual_invoice', cr)
sales_order_obj.write(cr, uid, [line.order_id.id], {'state': 'progress'})
if not invoices:

View File

@ -65,54 +65,28 @@ class sale_advance_payment_inv(osv.osv_memory):
return {'value': {'amount': product.list_price}}
return {'value': {'amount': 0}}
def create_invoices(self, cr, uid, ids, context=None):
""" create invoices for the active sale orders """
def _prepare_advance_invoice_vals(self, cr, uid, ids, context=None):
if context is None:
context = {}
sale_obj = self.pool.get('sale.order')
ir_property_obj = self.pool.get('ir.property')
fiscal_obj = self.pool.get('account.fiscal.position')
inv_line_obj = self.pool.get('account.invoice.line')
wizard = self.browse(cr, uid, ids[0], context)
sale_ids = context.get('active_ids', [])
if wizard.advance_payment_method == 'all':
# create the final invoices of the active sale orders
res = self.pool.get('sale.order').manual_invoice(cr, uid, sale_ids, context)
if context.get('open_invoices', False):
return res
return {'type': 'ir.actions.act_window_close'}
if wizard.advance_payment_method == 'lines':
# open the list view of sale order lines to invoice
act_window = self.pool.get('ir.actions.act_window')
res = act_window.for_xml_id(cr, uid, 'sale', 'action_order_line_tree2', context)
res['context'] = {
'search_default_uninvoiced': 1,
'search_default_order_id': sale_ids and sale_ids[0] or False,
}
return res
assert wizard.advance_payment_method in ('fixed', 'percentage')
sale_obj = self.pool.get('sale.order')
inv_obj = self.pool.get('account.invoice')
inv_line_obj = self.pool.get('account.invoice.line')
inv_ids = []
result = []
for sale in sale_obj.browse(cr, uid, sale_ids, context=context):
if sale.order_policy == 'postpaid':
raise osv.except_osv(
_('Error!'),
_("You cannot make an advance on a sales order \
that is defined as 'Automatic Invoice after delivery'."))
val = inv_line_obj.product_id_change(cr, uid, [], wizard.product_id.id,
uom=False, partner_id=sale.partner_id.id, fposition_id=sale.fiscal_position.id)
res = val['value']
# determine and check income account
if not wizard.product_id.id :
prop = self.pool.get('ir.property').get(cr, uid,
prop = ir_property_obj.get(cr, uid,
'property_account_income_categ', 'product.category', context=context)
prop_id = prop and prop.id or False
account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, sale.fiscal_position.id or False, prop_id)
account_id = fiscal_obj.map_account(cr, uid, sale.fiscal_position.id or False, prop_id)
if not account_id:
raise osv.except_osv(_('Configuration Error!'),
_('There is no income account defined as global property.'))
@ -171,29 +145,45 @@ class sale_advance_payment_inv(osv.osv_memory):
'payment_term': sale.payment_term.id,
'fiscal_position': sale.fiscal_position.id or sale.partner_id.property_account_position.id
}
inv_id = inv_obj.create(cr, uid, inv_values, context=context)
inv_obj.button_reset_taxes(cr, uid, [inv_id], context=context)
inv_ids.append(inv_id)
result.append((sale.id, inv_values))
return result
# add the invoice to the sale order's invoices
sale.write({'invoice_ids': [(4, inv_id)]})
def _create_invoices(self, cr, uid, inv_values, sale_id, context=None):
inv_obj = self.pool.get('account.invoice')
sale_obj = self.pool.get('sale.order')
inv_id = inv_obj.create(cr, uid, inv_values, context=context)
inv_obj.button_reset_taxes(cr, uid, [inv_id], context=context)
# add the invoice to the sale order's invoices
sale_obj.write(cr, uid, sale_id, {'invoice_ids': [(4, inv_id)]}, context=context)
return inv_id
# If invoice on picking: add the cost on the SO
# If not, the advance will be deduced when generating the final invoice
if sale.order_policy == 'picking':
vals = {
'order_id': sale.id,
'name': res.get('name'),
'price_unit': -inv_amount,
'product_uom_qty': wizard.qtty or 1.0,
'product_uos_qty': wizard.qtty or 1.0,
'product_uos': res.get('uos_id', False),
'product_uom': res.get('uom_id', False),
'product_id': wizard.product_id.id or False,
'discount': False,
'tax_id': res.get('invoice_line_tax_id'),
}
self.pool.get('sale.order.line').create(cr, uid, vals, context=context)
def create_invoices(self, cr, uid, ids, context=None):
""" create invoices for the active sale orders """
sale_obj = self.pool.get('sale.order')
act_window = self.pool.get('ir.actions.act_window')
wizard = self.browse(cr, uid, ids[0], context)
sale_ids = context.get('active_ids', [])
if wizard.advance_payment_method == 'all':
# create the final invoices of the active sale orders
res = sale_obj.manual_invoice(cr, uid, sale_ids, context)
if context.get('open_invoices', False):
return res
return {'type': 'ir.actions.act_window_close'}
if wizard.advance_payment_method == 'lines':
# open the list view of sale order lines to invoice
res = act_window.for_xml_id(cr, uid, 'sale', 'action_order_line_tree2', context)
res['context'] = {
'search_default_uninvoiced': 1,
'search_default_order_id': sale_ids and sale_ids[0] or False,
}
return res
assert wizard.advance_payment_method in ('fixed', 'percentage')
inv_ids = []
for sale_id, inv_values in self._prepare_advance_invoice_vals(cr, uid, ids, context=context):
inv_ids.append(self._create_invoices(cr, uid, inv_values, sale_id, context=context))
if context.get('open_invoices', False):
return self.open_invoices( cr, uid, ids, inv_ids, context=context)

View File

@ -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'],
'data': [
'wizard/crm_make_sale_view.xml',
'sale_crm_view.xml',

View File

@ -31,7 +31,7 @@ Price and Cost Price.
""",
'author':'OpenERP SA',
'images':['images/sale_margin.jpeg'],
'depends':['sale'],
'depends':['sale_stock'],
'demo':[],
'test': ['test/sale_margin.yml'],
'data':['security/ir.model.access.csv','sale_margin_view.xml'],

View File

@ -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'],
'data': [
'security/ir.model.access.csv',
'sale_mrp_view.xml',

View File

@ -37,7 +37,7 @@ You can add the following additional dates to a sale order:
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'images': ['images/sale_order_dates.jpeg'],
'depends': ['sale'],
'depends': ['sale_stock'],
'data': ['sale_order_dates_view.xml'],
'demo': [],
'test': [],

View File

@ -0,0 +1,28 @@
# -*- 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 report
import company
import res_config
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,69 @@
# -*- 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': 'Sales and Warehouse Management',
'version': '1.0',
'category': 'Hidden',
'summary': 'Quotation, Sale Orders, Delivery & Invoicing Control',
'description': """
Manage sales quotations and orders
==================================
This module makes the link between the sales and warehouses management applications.
Preferences
-----------
* Shipping: Choice of delivery at once or partial delivery
* Invoicing: choose how invoices will be paid
* Incoterms: International Commercial terms
You can choose flexible invoicing methods:
* *On Demand*: Invoices are created manually from Sales Orders when needed
* *On Delivery Order*: Invoices are generated from picking (delivery)
* *Before Delivery*: A Draft invoice is created and must be paid before delivery
""",
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'images': ['images/deliveries_to_invoice.jpeg'],
'depends': ['sale', 'stock', 'procurement'],
'init_xml': [],
'update_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/prepaid_order_policy.yml',
],
'installable': True,
'auto_install': True,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

2544
addons/sale_stock/i18n/ar.po Normal file

File diff suppressed because it is too large Load Diff

2634
addons/sale_stock/i18n/bg.po Normal file

File diff suppressed because it is too large Load Diff

2533
addons/sale_stock/i18n/bs.po Normal file

File diff suppressed because it is too large Load Diff

2996
addons/sale_stock/i18n/ca.po Normal file

File diff suppressed because it is too large Load Diff

2356
addons/sale_stock/i18n/cs.po Normal file

File diff suppressed because it is too large Load Diff

2197
addons/sale_stock/i18n/da.po Normal file

File diff suppressed because it is too large Load Diff

3053
addons/sale_stock/i18n/de.po Normal file

File diff suppressed because it is too large Load Diff

2696
addons/sale_stock/i18n/el.po Normal file

File diff suppressed because it is too large Load Diff

3005
addons/sale_stock/i18n/es.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2571
addons/sale_stock/i18n/et.po Normal file

File diff suppressed because it is too large Load Diff

2849
addons/sale_stock/i18n/fi.po Normal file

File diff suppressed because it is too large Load Diff

3105
addons/sale_stock/i18n/fr.po Normal file

File diff suppressed because it is too large Load Diff

2185
addons/sale_stock/i18n/gl.po Normal file

File diff suppressed because it is too large Load Diff

2873
addons/sale_stock/i18n/hr.po Normal file

File diff suppressed because it is too large Load Diff

2950
addons/sale_stock/i18n/hu.po Normal file

File diff suppressed because it is too large Load Diff

2668
addons/sale_stock/i18n/id.po Normal file

File diff suppressed because it is too large Load Diff

2176
addons/sale_stock/i18n/is.po Normal file

File diff suppressed because it is too large Load Diff

2938
addons/sale_stock/i18n/it.po Normal file

File diff suppressed because it is too large Load Diff

2288
addons/sale_stock/i18n/ja.po Normal file

File diff suppressed because it is too large Load Diff

2529
addons/sale_stock/i18n/ko.po Normal file

File diff suppressed because it is too large Load Diff

2158
addons/sale_stock/i18n/lo.po Normal file

File diff suppressed because it is too large Load Diff

2636
addons/sale_stock/i18n/lt.po Normal file

File diff suppressed because it is too large Load Diff

2406
addons/sale_stock/i18n/lv.po Normal file

File diff suppressed because it is too large Load Diff

2785
addons/sale_stock/i18n/mn.po Normal file

File diff suppressed because it is too large Load Diff

2647
addons/sale_stock/i18n/nb.po Normal file

File diff suppressed because it is too large Load Diff

3042
addons/sale_stock/i18n/nl.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2158
addons/sale_stock/i18n/oc.po Normal file

File diff suppressed because it is too large Load Diff

3062
addons/sale_stock/i18n/pl.po Normal file

File diff suppressed because it is too large Load Diff

3091
addons/sale_stock/i18n/pt.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3055
addons/sale_stock/i18n/ro.po Normal file

File diff suppressed because it is too large Load Diff

2830
addons/sale_stock/i18n/ru.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,650 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * sale_stock
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0alpha\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-09-20 07:32+0000\n"
"PO-Revision-Date: 2012-09-20 07:32+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: sale_stock
#: help:sale.config.settings,group_invoice_deli_orders:0
msgid "To allow your salesman to make invoices for Delivery Orders using the menu 'Deliveries to Invoice'."
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:496
#, python-format
msgid "Delivery Order <em>%s</em> <b>scheduled</b> for %s."
msgstr ""
#. module: sale_stock
#: model:process.node,name:sale_stock.process_node_deliveryorder0
msgid "Delivery Order"
msgstr ""
#. module: sale_stock
#: help:sale.order,picking_policy:0
msgid "If you don't have enough stock available to deliver all at once, do you accept partial shipments or not?"
msgstr ""
#. module: sale_stock
#: field:sale.order.line,type:0
msgid "Procurement Method"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:599
#, python-format
msgid "Picking Information ! : "
msgstr ""
#. module: sale_stock
#: model:process.node,name:sale_stock.process_node_packinglist0
msgid "Pick List"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:594
#, python-format
msgid "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"
msgstr ""
#. module: sale_stock
#: model:process.node,note:sale_stock.process_node_packinglist0
msgid "Document of the move to the output or to the customer."
msgstr ""
#. module: sale_stock
#: field:sale.config.settings,group_multiple_shops:0
msgid "Manage multiple shops"
msgstr ""
#. module: sale_stock
#: model:process.transition.action,name:sale_stock.process_transition_action_validate0
msgid "Validate"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:679
#, python-format
msgid "You cannot make an advance on a sales order that is defined as 'Automatic Invoice after delivery'."
msgstr ""
#. module: sale_stock
#: model:ir.ui.menu,name:sale_stock.menu_action_shop_form
msgid "Shop"
msgstr ""
#. module: sale_stock
#: model:process.transition,name:sale_stock.process_transition_saleprocurement0
msgid "Create Procurement Order"
msgstr ""
#. module: sale_stock
#: field:stock.picking.out,sale_id:0
msgid "Sale Order"
msgstr ""
#. module: sale_stock
#: model:process.transition,note:sale_stock.process_transition_saleorderprocurement0
msgid "For every sales order line, a procurement order is created to supply the sold product."
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:678
#, python-format
msgid "Error!"
msgstr ""
#. module: sale_stock
#: field:sale.order,picking_policy:0
msgid "Shipping Policy"
msgstr ""
#. module: sale_stock
#: help:sale.order,incoterm:0
msgid "Incoterm which stands for 'International Commercial terms' implies its a series of sales terms which are used in the commercial transaction."
msgstr ""
#. module: sale_stock
#: model:process.node,note:sale_stock.process_node_saleorderprocurement0
msgid "Drives procurement orders for every sales order line."
msgstr ""
#. module: sale_stock
#: model:ir.model,name:sale_stock.model_stock_move
msgid "Stock Move"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:164
#, python-format
msgid "Invalid Action!"
msgstr ""
#. module: sale_stock
#: field:sale.config.settings,module_project_timesheet:0
msgid "Project Timesheet"
msgstr ""
#. module: sale_stock
#: help:sale.config.settings,module_delivery:0
msgid "Allows you to add delivery methods in sale orders and delivery orders.\n"
" You can define your own carrier and delivery grids for prices.\n"
" This installs the module delivery."
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:647
#, python-format
msgid "Configuration Error!"
msgstr ""
#. module: sale_stock
#: model:process.node,name:sale_stock.process_node_saleprocurement0
msgid "Procurement Order"
msgstr ""
#. module: sale_stock
#: selection:sale.config.settings,default_order_policy:0
msgid "Invoice based on deliveries"
msgstr ""
#. module: sale_stock
#: model:ir.model,name:sale_stock.model_sale_order
#: field:stock.picking,sale_id:0
msgid "Sales Order"
msgstr ""
#. module: sale_stock
#: model:ir.model,name:sale_stock.model_stock_picking_out
#: view:sale.order:0
msgid "Delivery Orders"
msgstr ""
#. module: sale_stock
#: model:ir.model,name:sale_stock.model_sale_order_line
#: field:stock.move,sale_line_id:0
msgid "Sales Order Line"
msgstr ""
#. module: sale_stock
#: model:process.transition,note:sale_stock.process_transition_packing0
msgid "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."
msgstr ""
#. module: sale_stock
#: view:sale.order:0
msgid "Customer"
msgstr ""
#. module: sale_stock
#: constraint:res.company:0
msgid "Error! You can not create recursive companies."
msgstr ""
#. module: sale_stock
#: field:sale.config.settings,group_invoice_so_lines:0
msgid "Generate invoices based on the sale order"
msgstr ""
#. module: sale_stock
#: help:res.company,security_lead:0
msgid "This is the days added to what you promise to customers for security purpose"
msgstr ""
#. module: sale_stock
#: model:ir.model,name:sale_stock.model_stock_picking
msgid "Picking List"
msgstr ""
#. module: sale_stock
#: field:sale.shop,warehouse_id:0
msgid "Warehouse"
msgstr ""
#. module: sale_stock
#: model:process.transition.action,name:sale_stock.process_transition_action_forceassignation0
msgid "Force Assignation"
msgstr ""
#. module: sale_stock
#: field:sale.config.settings,default_order_policy:0
msgid "The default invoicing method is"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:233
#, python-format
msgid "Could not cancel sales order !"
msgstr ""
#. module: sale_stock
#: field:sale.order.line,delay:0
msgid "Delivery Lead Time"
msgstr ""
#. module: sale_stock
#: model:process.node,note:sale_stock.process_node_deliveryorder0
msgid "Document of the move to the customer."
msgstr ""
#. module: sale_stock
#: view:sale.order:0
msgid "View Delivery Order"
msgstr ""
#. module: sale_stock
#: field:sale.order.line,move_ids:0
msgid "Inventory Moves"
msgstr ""
#. module: sale_stock
#: view:sale.config.settings:0
msgid "Default Options"
msgstr ""
#. module: sale_stock
#: field:sale.config.settings,module_project_mrp:0
msgid "Project MRP"
msgstr ""
#. module: sale_stock
#: help:sale.order.line,type:0
msgid "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."
msgstr ""
#. module: sale_stock
#: model:process.transition,note:sale_stock.process_transition_invoiceafterdelivery0
msgid "The invoice is created automatically if the shipping policy is 'Invoice from pick' or 'Invoice on order after delivery'."
msgstr ""
#. module: sale_stock
#: field:sale.order.line,procurement_id:0
msgid "Procurement"
msgstr ""
#. module: sale_stock
#: view:sale.report:0
#: field:sale.report,shipped:0
#: field:sale.report,shipped_qty_1:0
msgid "Shipped"
msgstr ""
#. module: sale_stock
#: help:sale.config.settings,group_invoice_so_lines:0
msgid "To allow your salesman to make invoices for sale order lines using the menu 'Lines to Invoice'."
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:260
#, python-format
msgid "invalid mode for test_state"
msgstr ""
#. module: sale_stock
#: model:process.transition,note:sale_stock.process_transition_saleprocurement0
msgid "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. "
msgstr ""
#. module: sale_stock
#: field:sale.config.settings,group_invoice_deli_orders:0
msgid "Generate invoices after and based on delivery orders"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:499
#, python-format
msgid "Order <b>delivered</b>."
msgstr ""
#. module: sale_stock
#: field:sale.config.settings,module_delivery:0
msgid "Allow adding shipping costs"
msgstr ""
#. module: sale_stock
#: view:sale.order:0
msgid "days"
msgstr ""
#. module: sale_stock
#: field:sale.order.line,product_packaging:0
msgid "Packaging"
msgstr ""
#. module: sale_stock
#: field:sale.config.settings,default_picking_policy:0
msgid "Deliver all at once when all products are available."
msgstr ""
#. module: sale_stock
#: model:res.groups,name:sale_stock.group_invoice_deli_orders
msgid "Enable Invoicing Delivery orders"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:689
#, python-format
msgid "Advance of %s %%"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:601
#, python-format
msgid "Configuration Error !"
msgstr ""
#. module: sale_stock
#: field:res.company,security_lead:0
msgid "Security Days"
msgstr ""
#. module: sale_stock
#: model:process.transition,name:sale_stock.process_transition_saleorderprocurement0
msgid "Procurement of sold material"
msgstr ""
#. module: sale_stock
#: model:ir.model,name:sale_stock.model_sale_shop
msgid "Sales Shop"
msgstr ""
#. module: sale_stock
#: help:sale.order,shipped:0
msgid "It indicates that the sales order has been delivered. This field is updated only after the scheduler(s) have been launched."
msgstr ""
#. module: sale_stock
#: field:sale.order.line,property_ids:0
msgid "Properties"
msgstr ""
#. module: sale_stock
#: constraint:stock.move:0
msgid "You must assign a serial number for this product."
msgstr ""
#. module: sale_stock
#: field:sale.config.settings,group_mrp_properties:0
msgid "Product properties on order lines"
msgstr ""
#. module: sale_stock
#: help:sale.config.settings,default_order_policy:0
msgid "You can generate invoices based on sales orders or based on shippings."
msgstr ""
#. module: sale_stock
#: constraint:stock.move:0
msgid "You cannot move products from or to a location of the type view."
msgstr ""
#. module: sale_stock
#: model:ir.model,name:sale_stock.model_sale_report
msgid "Sales Orders Statistics"
msgstr ""
#. module: sale_stock
#: model:ir.model,name:sale_stock.model_res_company
msgid "Companies"
msgstr ""
#. module: sale_stock
#: help:sale.config.settings,task_work:0
msgid "Lets you transfer the entries under tasks defined for Project Management to\n"
" the Timesheet line entries for particular date and particular user with the effect of creating, editing and deleting either ways\n"
" and to automatically creates project tasks from procurement lines.\n"
" This installs the modules project_timesheet and project_mrp."
msgstr ""
#. module: sale_stock
#: view:sale.order:0
msgid "View Invoice"
msgstr ""
#. module: sale_stock
#: sql_constraint:stock.picking:0
#: sql_constraint:stock.picking.out:0
msgid "Reference must be unique per Company!"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:696
#: code:addons/sale_stock/sale_stock.py:698
#, python-format
msgid "Advance of %s %s"
msgstr ""
#. module: sale_stock
#: model:process.node,note:sale_stock.process_node_saleprocurement0
msgid "One Procurement order for each sales order line and for each of the components."
msgstr ""
#. module: sale_stock
#: model:process.transition.action,name:sale_stock.process_transition_action_assign0
msgid "Assign"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:643
#, python-format
msgid "Not enough stock ! : "
msgstr ""
#. module: sale_stock
#: help:sale.order.line,delay:0
msgid "Number of days between the order confirmation the shipping of the products to the customer"
msgstr ""
#. module: sale_stock
#: selection:sale.order.line,type:0
msgid "from stock"
msgstr ""
#. module: sale_stock
#: help:sale.config.settings,default_picking_policy:0
msgid "Sales order by default will be configured to deliver all products at once instead of delivering each product when it is available. This may have an impact on the shipping price."
msgstr ""
#. module: sale_stock
#: selection:sale.config.settings,default_order_policy:0
msgid "Invoice based on sales orders"
msgstr ""
#. module: sale_stock
#: model:process.node,name:sale_stock.process_node_invoiceafterdelivery0
msgid "Invoice"
msgstr ""
#. module: sale_stock
#: model:process.transition.action,name:sale_stock.process_transition_action_cancel1
#: model:process.transition.action,name:sale_stock.process_transition_action_cancel2
#: view:sale.order:0
msgid "Cancel"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:164
#, python-format
msgid "In order to delete a confirmed sales order, you must cancel it.\n"
"To do so, you must first cancel related picking for delivery orders."
msgstr ""
#. module: sale_stock
#: field:sale.order.line,number_packages:0
msgid "Number Packages"
msgstr ""
#. module: sale_stock
#: sql_constraint:sale.order:0
msgid "Order Reference must be unique per Company!"
msgstr ""
#. module: sale_stock
#: field:sale.order,shipped:0
msgid "Delivered"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:234
#, python-format
msgid "You must first cancel all picking attached to this sales order."
msgstr ""
#. module: sale_stock
#: model:process.transition,name:sale_stock.process_transition_invoiceafterdelivery0
msgid "Create Invoice"
msgstr ""
#. module: sale_stock
#: help:sale.config.settings,group_mrp_properties:0
msgid "Allows you to tag sale order lines with properties."
msgstr ""
#. module: sale_stock
#: field:sale.config.settings,task_work:0
msgid "Prepare invoices based on task's activities"
msgstr ""
#. module: sale_stock
#: model:ir.model,name:sale_stock.model_sale_advance_payment_inv
msgid "Sales Advance Payment Invoice"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:660
#, python-format
msgid "You must first cancel stock moves attached to this sales order line."
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:590
#, python-format
msgid "(n/a)"
msgstr ""
#. module: sale_stock
#: field:sale.order,incoterm:0
msgid "Incoterm"
msgstr ""
#. module: sale_stock
#: constraint:stock.move:0
msgid "You try to assign a lot which is not from the same product."
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:659
#, python-format
msgid "Cannot cancel sales order line!"
msgstr ""
#. module: sale_stock
#: view:sale.order:0
msgid "Deliver"
msgstr ""
#. module: sale_stock
#: model:process.transition.action,name:sale_stock.process_transition_action_cancelassignation0
msgid "Cancel Assignation"
msgstr ""
#. module: sale_stock
#: selection:sale.order.line,type:0
msgid "on order"
msgstr ""
#. module: sale_stock
#: model:process.node,note:sale_stock.process_node_invoiceafterdelivery0
msgid "Based on the shipped or on the ordered quantities."
msgstr ""
#. module: sale_stock
#: selection:sale.order,picking_policy:0
msgid "Deliver all products at once"
msgstr ""
#. module: sale_stock
#: field:sale.order,picking_ids:0
msgid "Related Picking"
msgstr ""
#. module: sale_stock
#: sql_constraint:res.company:0
msgid "The company name must be unique !"
msgstr ""
#. module: sale_stock
#: model:ir.model,name:sale_stock.model_sale_config_settings
msgid "sale.config.settings"
msgstr ""
#. module: sale_stock
#: help:sale.order,picking_ids:0
msgid "This is a list of delivery orders that has been generated for this sales order."
msgstr ""
#. module: sale_stock
#: model:process.node,name:sale_stock.process_node_saleorderprocurement0
msgid "Sales Order Requisition"
msgstr ""
#. module: sale_stock
#: model:process.transition,name:sale_stock.process_transition_deliver0
msgid "Create Delivery Order"
msgstr ""
#. module: sale_stock
#: view:sale.order:0
msgid "Ignore Exception"
msgstr ""
#. module: sale_stock
#: code:addons/sale_stock/sale_stock.py:639
#, python-format
msgid "You plan to sell %.2f %s but you only have %.2f %s available !\n"
"The real stock is %.2f %s. (without reservations)"
msgstr ""
#. module: sale_stock
#: view:sale.order:0
msgid "Recreate Delivery Order"
msgstr ""
#. module: sale_stock
#: help:sale.config.settings,group_multiple_shops:0
msgid "This allows to configure and use multiple shops."
msgstr ""
#. module: sale_stock
#: field:sale.order,picked_rate:0
msgid "Picked"
msgstr ""
#. module: sale_stock
#: selection:sale.order,picking_policy:0
msgid "Deliver each product when available"
msgstr ""
#. module: sale_stock
#: model:process.transition,name:sale_stock.process_transition_packing0
msgid "Create Pick List"
msgstr ""
#. module: sale_stock
#: model:process.transition,note:sale_stock.process_transition_deliver0
msgid "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."
msgstr ""

2350
addons/sale_stock/i18n/sk.po Normal file

File diff suppressed because it is too large Load Diff

2530
addons/sale_stock/i18n/sl.po Normal file

File diff suppressed because it is too large Load Diff

2158
addons/sale_stock/i18n/sq.po Normal file

File diff suppressed because it is too large Load Diff

2751
addons/sale_stock/i18n/sr.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2955
addons/sale_stock/i18n/sv.po Normal file

File diff suppressed because it is too large Load Diff

2194
addons/sale_stock/i18n/th.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2853
addons/sale_stock/i18n/tr.po Normal file

File diff suppressed because it is too large Load Diff

2362
addons/sale_stock/i18n/uk.po Normal file

File diff suppressed because it is too large Load Diff

2630
addons/sale_stock/i18n/vi.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,170 @@
<?xml version="1.0" ?>
<openerp>
<data>
<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="&quot;&quot;&quot;subflow&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;Sales Order Requisition&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;Drives procurement orders for every sales order line.&quot;&quot;&quot;" name="note"/>
<field name="subflow_id" ref="sale.process_process_salesprocess0"/>
<field name="process_id" ref="procurement.process_process_procurementprocess0"/>
<field eval="&quot;&quot;&quot;object.state in ('draft', 'waiting_date', 'manual', 'progress', 'shipping_except', 'invoice_except', 'done', 'cancel')&quot;&quot;&quot;" 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="&quot;&quot;&quot;subflow&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;Procurement Order&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;One Procurement order for each sales order line and for each of the components.&quot;&quot;&quot;" name="note"/>
<field name="subflow_id" ref="procurement.process_process_procurementprocess0"/>
<field name="process_id" ref="sale.process_process_salesprocess0"/>
<field eval="&quot;&quot;&quot;object.state=='confirmed'&quot;&quot;&quot;" 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="&quot;&quot;&quot;state&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;Pick List&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;Document of the move to the output or to the customer.&quot;&quot;&quot;" name="note"/>
<field name="process_id" ref="sale.process_process_salesprocess0"/>
<field eval="&quot;&quot;&quot;object.state in ('assigned', 'confirmed')&quot;&quot;&quot;" 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="&quot;&quot;&quot;state&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;Delivery Order&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;Document of the move to the customer.&quot;&quot;&quot;" name="note"/>
<field name="process_id" ref="sale.process_process_salesprocess0"/>
<field eval="&quot;&quot;&quot;object.state in ('done', 'assigned')&quot;&quot;&quot;" name="model_states"/>
<field eval="0" name="flow_start"/>
</record>
<record id="process_node_invoiceafterdelivery0" model="process.node">
<field name="menu_id" ref="account.menu_action_invoice_tree1"/>
<field name="model_id" ref="account.model_account_invoice"/>
<field eval="&quot;&quot;&quot;subflow&quot;&quot;&quot;" name="kind"/>
<field eval="&quot;&quot;&quot;Invoice&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;Based on the shipped or on the ordered quantities.&quot;&quot;&quot;" name="note"/>
<field name="process_id" ref="sale.process_process_salesprocess0"/>
<field name="subflow_id" ref="account.process_process_invoiceprocess0"/>
<field eval="&quot;&quot;&quot;object.state=='draft'&quot;&quot;&quot;" name="model_states"/>
<field eval="0" name="flow_start"/>
</record>
<!--
Process Condition
-->
<record id="process_condition_conditionafterdelivery0" model="process.condition">
<field name="model_id" ref="sale.model_sale_order"/>
<field name="node_id" ref="process_node_invoiceafterdelivery0"/>
<field eval="&quot;&quot;&quot;object.order_policy=='postpaid'&quot;&quot;&quot;" name="model_states"/>
<field eval="&quot;&quot;&quot;condition_after_delivery&quot;&quot;&quot;" name="name"/>
</record>
<!--
Process Transition
-->
<record id="process_transition_saleprocurement0" model="process.transition">
<field eval="[(6,0,[])]" name="transition_ids"/>
<field eval="&quot;&quot;&quot;Create Procurement Order&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;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. &quot;&quot;&quot;" 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="&quot;&quot;&quot;Create Pick List&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;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.&quot;&quot;&quot;" 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="&quot;&quot;&quot;Create Delivery Order&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;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.&quot;&quot;&quot;" 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_invoiceafterdelivery0" model="process.transition">
<field eval="[(6,0,[])]" name="transition_ids"/>
<field eval="&quot;&quot;&quot;Create Invoice&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;The invoice is created automatically if the shipping policy is 'Invoice from pick' or 'Invoice on order after delivery'.&quot;&quot;&quot;" 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="&quot;&quot;&quot;Procurement of sold material&quot;&quot;&quot;" name="name"/>
<field eval="&quot;&quot;&quot;For every sales order line, a procurement order is created to supply the sold product.&quot;&quot;&quot;" 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>
<!--
Process Action
-->
<record id="process_transition_action_assign0" model="process.transition.action">
<field eval="&quot;&quot;&quot;action_assign&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Assign&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_packing0"/>
</record>
<record id="process_transition_action_forceassignation0" model="process.transition.action">
<field eval="&quot;&quot;&quot;force_assign&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Force Assignation&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_packing0"/>
</record>
<record id="process_transition_action_cancel1" model="process.transition.action">
<field eval="&quot;&quot;&quot;action_cancel&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Cancel&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_packing0"/>
</record>
<record id="process_transition_action_cancelassignation0" model="process.transition.action">
<field eval="&quot;&quot;&quot;cancel_assign&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Cancel Assignation&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_deliver0"/>
</record>
<record id="process_transition_action_validate0" model="process.transition.action">
<field eval="&quot;&quot;&quot;test_finished&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Validate&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_deliver0"/>
</record>
<record id="process_transition_action_cancel2" model="process.transition.action">
<field eval="&quot;&quot;&quot;action_cancel&quot;&quot;&quot;" name="action"/>
<field eval="&quot;&quot;&quot;object&quot;&quot;&quot;" name="state"/>
<field eval="&quot;&quot;&quot;Cancel&quot;&quot;&quot;" name="name"/>
<field name="transition_id" ref="process_transition_deliver0"/>
</record>
</data>
</openerp>

View File

@ -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

View File

@ -0,0 +1,96 @@
# -*- 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()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -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>

View File

@ -0,0 +1,90 @@
# -*- 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('Generate invoices after and based on delivery orders',
implied_group='sale_stock.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("Prepare invoices based on task's 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')],
'The default invoicing method is', default_model='sale.order',
help="You can generate invoices based on sales orders or based on shippings."),
'module_delivery': fields.boolean('Allow adding 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("Deliver all at once when all products are available.",
help = "Sales order by default will be configured to deliver all products at once instead of delivering each product when it is available. This may have an impact on the shipping price."),
'group_mrp_properties': fields.boolean('Product properties on order 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"),
}
_defaults = {
'default_order_policy': 'manual',
}
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 {}

View File

@ -0,0 +1,75 @@
<?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>
<xpath expr="//div[@name='timesheet']" position="before">
<div>
<field name="group_invoice_so_lines" on_change="onchange_invoice_methods(group_invoice_so_lines, group_invoice_deli_orders)" class="oe_inline"/>
<label for="group_invoice_so_lines"/>
</div>
<div>
<field name="group_invoice_deli_orders" class="oe_inline" on_change="onchange_invoice_methods(group_invoice_so_lines, group_invoice_deli_orders)"/>
<label for="group_invoice_deli_orders"/>
</div>
<div>
<field name="task_work" class="oe_inline" on_change="onchange_task_work(task_work)"/>
<label for="task_work"/>
</div>
</xpath>
<xpath expr="//label[@string='Default Options']" position="replace">
<label for="id" string="Default Options" attrs="{'invisible':['|',('group_invoice_so_lines','=',False),('group_invoice_deli_orders','=',False),('group_invoice_so_lines','=',False), ('group_invoice_deli_orders','=',False),('task_work','=',False), ('module_account_analytic_analysis','=',False)]}"/>
</xpath>
<xpath expr="//div[@name='time_unit']" position="before">
<div attrs="{'invisible':['|',('group_invoice_so_lines','=',False),('group_invoice_deli_orders','=',False)],'required': ['|',('group_invoice_so_lines','=',True),('group_invoice_deli_orders','=',True)]}">
<label for="default_order_policy"/>
<field name="default_order_policy" class="oe_inline"/>
</div>
<div attrs="{'invisible':[('group_invoice_so_lines','=',False), ('group_invoice_deli_orders','=',False)]}">
<field name="module_delivery" class="oe_inline"/>
<label for="module_delivery"/>
</div>
</xpath>
<group name='default_options' position="after">
<field name="module_project_timesheet" invisible="1"/>
<field name="module_project_mrp" invisible="1"/>
</group>
<div name='warehouse_features' position='inside'>
<div name='default_picking_policy' attrs="{'invisible':[('group_invoice_deli_orders','=',False)]}">
<field name="default_picking_policy" class="oe_inline"/>
<label for="default_picking_policy"/>
</div>
</div>
<div name='module_sale_journal' position="before">
<div>
<field name="group_mrp_properties" class="oe_inline"/>
<label for="group_mrp_properties"/>
</div>
</div>
<xpath expr="//div[@name='module_sale_margin']" position="before">
<div>
<field name="group_multiple_shops" class="oe_inline"/>
<label for="group_multiple_shops"/>
</div>
</xpath>
<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)" class="oe_inline"/>
</field>
<xpath expr="//div[@name='time_unit']" position="replace">
<div attrs="{'invisible': [('task_work','=',False), ('module_account_analytic_analysis','=',False)],'required': ['|', ('task_work','=',True), ('module_account_analytic_analysis','=',True)]}">
<label for="time_unit"/>
<field name="time_unit" domain="[('category_id.name','=','Working Time')]" class="oe_inline"/>
</div>
</xpath>
</data>
</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,680 @@
# -*- 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'),
}
sale_shop()
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 sales order, you must cancel it.\nTo do so, you must first cancel related picking for delivery orders.'))
return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
def onchange_shop_id(self, cr, uid, ids, shop_id):
v = {}
if shop_id:
shop = self.pool.get('sale.shop').browse(cr, uid, shop_id)
v['project_id'] = shop.project_id.id
# Que faire si le client a une pricelist a lui ?
if shop.pricelist_id.id:
v['pricelist_id'] = shop.pricelist_id.id
return {'value': v}
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')
act_obj = self.pool.get('ir.actions.act_window')
result = mod_obj.get_object_reference(cr, uid, 'stock', 'action_picking_tree')
id = result and result[1] or False
result = act_obj.read(cr, uid, [id], context=context)[0]
#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:
result['domain'] = "[('id','in',["+','.join(map(str, pick_ids))+"])]"
else:
res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_out_form')
result['views'] = [(res and res[1] or False, 'form')]
result['res_id'] = pick_ids and pick_ids[0] or False
return result
def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed', 'done', 'exception'], date_inv = False, context=None):
picking_obj = self.pool.get('stock.picking')
res = super(sale_order,self).action_invoice_create( cr, uid, ids, grouped=grouped, states=states, date_inv = date_inv, context=context)
for order in self.browse(cr, uid, ids, context=context):
if order.order_policy == 'picking':
picking_obj.write(cr, uid, map(lambda x: x.id, order.picking_ids), {'invoice_state': 'invoiced'})
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(
_('Cannot cancel sales order!'),
_('You must first cancel all delivery order(s) 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 action_wait(self, cr, uid, ids, context=None):
res = super(sale_order, self).action_wait(cr, uid, ids, context=context)
for o in self.browse(cr, uid, ids):
noprod = self.test_no_product(cr, uid, o, context)
if noprod and o.order_policy=='picking':
self.write(cr, uid, [o.id], {'order_policy': 'manual'}, context=context)
return res
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:])
}
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)
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_post(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_post(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 _get_line_qty(self, cr, uid, line, context=None):
if line.procurement_id and not (line.order_id.invoice_quantity=='order'):
return self.pool.get('procurement.order').quantity_get(cr, uid,
line.procurement_id.id, context=context)
else:
return super(sale_order_line, self)._get_line_qty(cr, uid, line, context=context)
def _get_line_uom(self, cr, uid, line, context=None):
if line.procurement_id and not (line.order_id.invoice_quantity=='order'):
return self.pool.get('procurement.order').uom_get(cr, uid,
line.procurement_id.id, context=context)
else:
return super(sale_order_line, self)._get_line_uom(cr, uid, line, context=context)
def button_cancel(self, cr, uid, ids, context=None):
res = super(sale_order_line, self).button_cancel(cr, uid, ids, context=context)
for line in self.browse(cr, uid, ids, context=context):
for move_line in line.move_ids:
if move_line.state != 'cancel':
raise osv.except_osv(
_('Cannot cancel sales order line!'),
_('You must first cancel stock moves attached to this sales order line.'))
return res
def copy_data(self, cr, uid, id, default=None, context=None):
if not default:
default = {}
default.update({'move_ids': []})
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,
lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
context = context or {}
product_uom_obj = self.pool.get('product.uom')
partner_obj = self.pool.get('res.partner')
product_obj = self.pool.get('product.product')
warning = {}
res = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty=qty,
uom=uom, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,
lang=lang, update_tax=update_tax, date_order=date_order, packaging=packaging, fiscal_position=fiscal_position, flag=flag, context=context)
if not product:
return {'value': {'th_weight': 0, 'product_packaging': False,
'product_uos_qty': qty}, 'domain': {'product_uom': [],
'product_uos': []}}
res_packing = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging, context=context)
res['value'].update(res_packing.get('value', {}))
warning_msgs = res_packing.get('warning') and res_packing['warning']['message'] or ''
product_obj = product_obj.browse(cr, uid, product, context=context)
res['value']['delay'] = (product_obj.sale_delay or 0.0)
uom2 = False
if uom:
uom2 = product_uom_obj.browse(cr, uid, uom)
if product_obj.uom_id.category_id.id != uom2.category_id.id:
uom = False
if not uom2:
uom2 = product_obj.uom_id
compare_qty = float_compare(product_obj.virtual_available * uom2.factor, qty * product_obj.uom_id.factor, precision_rounding=product_obj.uom_id.rounding)
if (product_obj.type=='product') and int(compare_qty) == -1 \
and (product_obj.procure_method=='make_to_stock'):
warn_msg = _('You plan to sell %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') % \
(qty, uom2 and uom2.name or product_obj.uom_id.name,
max(0,product_obj.virtual_available), product_obj.uom_id.name,
max(0,product_obj.qty_available), product_obj.uom_id.name)
warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n"
# get unit price
if warning_msgs:
warning = {
'title': _('Configuration Error!'),
'message' : warning_msgs
}
res.update({'warning': warning})
return res
class sale_advance_payment_inv(osv.osv_memory):
_inherit = "sale.advance.payment.inv"
def _create_invoices(self, cr, uid, inv_values, sale_id, context=None):
result = super(sale_advance_payment_inv, self)._create_invoices(cr, uid, inv_values, sale_id, context=context)
sale_obj = self.pool.get('sale.order')
sale_line_obj = self.pool.get('sale.order.line')
wizard = self.browse(cr, uid, ids[0], context)
sale = sale_obj.browse(cr, uid, sale_id, context=context)
if sale.order_policy == 'postpaid':
raise osv.except_osv(
_('Error!'),
_("You cannot make an advance on a sales order \
that is defined as 'Automatic Invoice after delivery'."))
# If invoice on picking: add the cost on the SO
# If not, the advance will be deduced when generating the final invoice
line_name = inv_values.get('invoice_line') and inv_values.get('invoice_line')[2].get('name') or ''
line_tax = inv_values.get('invoice_line') and inv_values.get('invoice_line')[2].get('invoice_line_tax_id') or False
if sale.order_policy == 'picking':
vals = {
'order_id': sale.id,
'name': line_name,
'price_unit': -inv_amount,
'product_uom_qty': wizard.qtty or 1.0,
'product_uos_qty': wizard.qtty or 1.0,
'product_uos': res.get('uos_id', False),
'product_uom': res.get('uom_id', False),
'product_id': wizard.product_id.id or False,
'discount': False,
'tax_id': line_tax,
}
sale_line_obj.create(cr, uid, vals, context=context)
return result

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<!-- Resource: sale.shop -->
<record id="sale.sale_shop_1" model="sale.shop">
<field name="warehouse_id" ref="stock.warehouse0"/>
</record>
</data>
</openerp>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="sale.sale_order_1" model="sale.order">
<field name="order_policy">prepaid</field>
</record>
<record id="sale.sale_order_4" model="sale.order">
<field name="order_policy">prepaid</field>
</record>
<record id="sale.sale_order_5" model="sale.order">
<field name="order_policy">picking</field>
</record>
<record id="sale.sale_order_6" model="sale.order">
<field name="order_policy">picking</field>
</record>
<!-- Confirm some Sale Orders-->
<workflow action="order_confirm" model="sale.order" ref="sale.sale_order_1"/>
<workflow action="order_confirm" model="sale.order" ref="sale.sale_order_5"/>
<record id="sale.sale_order_line_4" model="sale.order.line">
<field name="type">make_to_order</field>
</record>
<record id="sale.sale_order_line_5" model="sale.order.line">
<field name="type">make_to_order</field>
</record>
<record id="sale.sale_order_line_6" model="sale.order.line">
<field name="type">make_to_order</field>
</record>
<record id="sale.sale_order_line_8" model="sale.order.line">
<field name="type">make_to_order</field>
</record>
<!-- Run all schedulers -->
<function model="procurement.order" name="run_scheduler"/>
</data>
</openerp>

View File

@ -0,0 +1,122 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="view_sale_shop_form_inherit" model="ir.ui.view">
<field name="name">sale.shop.inherit.form</field>
<field name="model">sale.shop</field>
<field name="inherit_id" ref="sale.view_shop_form"/>
<field name="arch" type="xml">
<xpath expr="//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>
<menuitem action="sale.action_shop_form" id="menu_action_shop_form" parent="base.menu_base_config" sequence="35" groups="stock.group_locations"/>
<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="//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="//button[@name='action_view_invoice']" position="after">
<button name="action_view_delivery" string="View Delivery Order" type="object" class="oe_highlight"
attrs="{'invisible': ['|','|','|',('picking_ids','=',False),('picking_ids','=',[]), ('state', 'not in', ('progress','manual')),('shipped','=',True)]}"/> </xpath>
<xpath expr="//button[@name='action_cancel']" position="after">
<button name="ship_cancel" states="shipping_except" string="Cancel"/>
</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="shop_id" position="replace">
<field name="shop_id" on_change="onchange_shop_id(shop_id)" widget="selection" groups="stock.group_locations"/>
</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='invoiced' position="after">
<field name="shipped"/>
<field name="invoice_exists" invisible="1"/>
</field>
<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 &lt; 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>
<xpath expr="//page[@string='Order Lines']/field[@name='order_line']/form[@string='Sales Order Lines']/group/group/field[@name='th_weight']" position="before">
<label for="delay"/>
<div>
<field name="delay" class="oe_inline"/> days
</div>
<field name="type"/>
</xpath>
<xpath expr="//page[@string='Order Lines']/field[@name='order_line']/form[@string='Sales Order Lines']/group/group/field[@name='address_allotment_id']" position="after">
<field name="property_ids" widget="many2many_tags"
groups="sale.group_mrp_properties"/>
</xpath>
<xpath expr="//page[@string='Order Lines']/field[@name='order_line']/form[@string='Sales Order Lines']/group/group/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"/>
</xpath>
<xpath expr="//page[@string='Order Lines']/field[@name='order_line']/form[@string='Sales Order Lines']/div/field[@name='invoice_lines']" position="after">
<label for="move_ids"/>
<field name="move_ids" widget="many2many"/>
</xpath>
<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="//group[@name='sales_person']" position="before">
<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="invoice_quantity" invisible="True"/>
</group>
</xpath>
</data>
</field>
</record>
</data>
</openerp>

Some files were not shown because too many files have changed in this diff Show More