[IMP] sale: adaptation due to the new Sale module
Invoicing is now completely handled from the SO. cf. documentation Reason: complete rewrite of the Sale module. Responsible: fp, dbo, nim
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import sale
|
||||
import sale_analytic
|
||||
import sales_team
|
||||
import res_partner
|
||||
import wizard
|
||||
|
||||
@@ -42,11 +42,8 @@ The Dashboard for the Sales Manager will include
|
||||
'depends': ['sales_team','account', 'procurement', 'report'],
|
||||
'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',
|
||||
'sale_workflow.xml',
|
||||
'sale_sequence.xml',
|
||||
'sale_report.xml',
|
||||
'sale_data.xml',
|
||||
@@ -61,16 +58,9 @@ The Dashboard for the Sales Manager will include
|
||||
'sales_team_dashboard.xml',
|
||||
'sale_tip_data.xml',
|
||||
],
|
||||
'demo': ['sale_demo.xml'],
|
||||
'test': [
|
||||
'../account/test/account_minimal_test.xml',
|
||||
'test/create_sale_users.yml',
|
||||
'test/sale_order_demo.yml',
|
||||
'test/manual_order_policy.yml',
|
||||
'test/cancel_order.yml',
|
||||
'test/delete_order.yml',
|
||||
'test/canceled_lines_order.yml',
|
||||
],
|
||||
'demo': ['sale_demo.xml',
|
||||
'sale_product_demo.xml',
|
||||
],
|
||||
'css': ['static/src/css/sale.css'],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from openerp.osv import fields,osv
|
||||
|
||||
class account_invoice_report(osv.osv):
|
||||
from openerp import api, fields, models, _
|
||||
|
||||
class AccountInvoiceReport(models.Model):
|
||||
_inherit = 'account.invoice.report'
|
||||
_columns = {
|
||||
'team_id': fields.many2one('crm.team', 'Sales Team', oldname='section_id'),
|
||||
}
|
||||
_depends = {
|
||||
'account.invoice': ['team_id'],
|
||||
}
|
||||
team_id = fields.Many2one('crm.team', string='Sales Team')
|
||||
|
||||
def _select(self):
|
||||
return super(account_invoice_report, self)._select() + ", sub.team_id as team_id"
|
||||
return super(AccountInvoiceReport, self)._select() + ", sub.team_id as team_id"
|
||||
|
||||
def _sub_select(self):
|
||||
return super(account_invoice_report, self)._sub_select() + ", ai.team_id as team_id"
|
||||
return super(AccountInvoiceReport, self)._sub_select() + ", ai.team_id as team_id"
|
||||
|
||||
def _group_by(self):
|
||||
return super(account_invoice_report, self)._group_by() + ", ai.team_id"
|
||||
return super(AccountInvoiceReport, self)._group_by() + ", ai.team_id"
|
||||
|
||||
@@ -11,33 +11,32 @@ class sale_report(osv.osv):
|
||||
_rec_name = 'date'
|
||||
|
||||
_columns = {
|
||||
'date': fields.datetime('Date Order', readonly=True), # TDE FIXME master: rename into date_order
|
||||
'date_confirm': fields.date('Date Confirm', readonly=True),
|
||||
'date': fields.datetime('Date Order', readonly=True),
|
||||
'product_id': fields.many2one('product.product', 'Product', readonly=True),
|
||||
'product_uom': fields.many2one('product.uom', 'Unit of Measure', readonly=True),
|
||||
'product_uom_qty': fields.float('# of Qty', readonly=True),
|
||||
|
||||
'qty_delivered': fields.float('Qty Delivered', readonly=True),
|
||||
'qty_to_invoice': fields.float('Qty To Invoice', readonly=True),
|
||||
'qty_invoiced': fields.float('Qty Invoiced', readonly=True),
|
||||
'partner_id': fields.many2one('res.partner', 'Partner', readonly=True),
|
||||
'company_id': fields.many2one('res.company', 'Company', readonly=True),
|
||||
'user_id': fields.many2one('res.users', 'Salesperson', readonly=True),
|
||||
'price_total': fields.float('Total Price', readonly=True),
|
||||
'delay': fields.float('Commitment Delay', digits=(16,2), readonly=True),
|
||||
'product_tmpl_id': fields.many2one('product.template', 'Product Template', readonly=True),
|
||||
'categ_id': fields.many2one('product.category','Product Category', readonly=True),
|
||||
'nbr': fields.integer('# of Lines', readonly=True), # TDE FIXME master: rename into nbr_lines
|
||||
'state': fields.selection([
|
||||
('cancel', 'Cancelled'),
|
||||
('draft', 'Draft'),
|
||||
('confirmed', 'Confirmed'),
|
||||
('exception', 'Exception'),
|
||||
('done', 'Done')], 'Order Status', readonly=True),
|
||||
'nbr': fields.integer('# of Lines', readonly=True),
|
||||
'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', readonly=True),
|
||||
'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
|
||||
'invoiced': fields.boolean('Paid', readonly=True),
|
||||
'nbr_paid': fields.integer('# of Paid Lines', readonly=True),
|
||||
'team_id': fields.many2one('crm.team', 'Sales Team', readonly=True, oldname='section_id'),
|
||||
'country_id': fields.many2one('res.country', 'Partner Country', readonly=True),
|
||||
'commercial_partner_id': fields.many2one('res.partner', 'Commercial Entity', readonly=True),
|
||||
'state': fields.selection([
|
||||
('draft', 'Draft Quotation'),
|
||||
('sent', 'Quotation Sent'),
|
||||
('sale', 'Sales Order'),
|
||||
('done', 'Sales Done'),
|
||||
('cancel', 'Cancelled'),
|
||||
], string='Status', readonly=True),
|
||||
}
|
||||
_order = 'date desc'
|
||||
|
||||
@@ -56,22 +55,23 @@ class sale_report(osv.osv):
|
||||
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 * cr.rate * l.price_unit * (100.0-l.discount) / 100.0) as price_total,
|
||||
sum(l.qty_delivered / u.factor * u2.factor) as qty_delivered,
|
||||
sum(l.qty_invoiced / u.factor * u2.factor) as qty_invoiced,
|
||||
sum(l.qty_to_invoice / u.factor * u2.factor) as qty_to_invoice,
|
||||
sum(l.price_total * cr.rate) as price_total,
|
||||
sum(l.price_subtotal * cr.rate) as price_subtotal,
|
||||
count(*) as nbr,
|
||||
s.date_order as date,
|
||||
s.date_confirm as date_confirm,
|
||||
s.state as state,
|
||||
s.partner_id as partner_id,
|
||||
s.user_id as user_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,
|
||||
l.state,
|
||||
extract(epoch from avg(date_trunc('day',s.date_order)-date_trunc('day',s.create_date)))/(24*60*60)::decimal(16,2) as delay,
|
||||
t.categ_id as categ_id,
|
||||
s.pricelist_id as pricelist_id,
|
||||
s.project_id as analytic_account_id,
|
||||
s.team_id as team_id,
|
||||
p.product_tmpl_id,
|
||||
l.invoiced::integer as nbr_paid,
|
||||
l.invoiced,
|
||||
partner.country_id as country_id,
|
||||
partner.commercial_partner_id as commercial_partner_id
|
||||
"""
|
||||
@@ -100,16 +100,14 @@ class sale_report(osv.osv):
|
||||
t.uom_id,
|
||||
t.categ_id,
|
||||
s.date_order,
|
||||
s.date_confirm,
|
||||
s.partner_id,
|
||||
s.user_id,
|
||||
s.state,
|
||||
s.company_id,
|
||||
l.state,
|
||||
s.pricelist_id,
|
||||
s.project_id,
|
||||
s.team_id,
|
||||
p.product_tmpl_id,
|
||||
l.invoiced,
|
||||
partner.country_id,
|
||||
partner.commercial_partner_id
|
||||
"""
|
||||
|
||||
@@ -58,10 +58,9 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Sales Analysis">
|
||||
<field name="date"/>
|
||||
<field name="date_confirm"/>
|
||||
<filter string="This Year" name="year" invisible="1" domain="[('date','<=', time.strftime('%%Y-12-31')),('date','>=',time.strftime('%%Y-01-01'))]"/>
|
||||
<filter name="Quotations" domain="[('state','in',('draft'))]"/>
|
||||
<filter name="Sales" string="Sales" domain="[('state','not in',('draft', 'cancel'))]"/>
|
||||
<filter name="Sales" string="Sales" domain="[('state','not in',('draft', 'cancel', 'sent'))]"/>
|
||||
<separator/>
|
||||
<field name="partner_id"/>
|
||||
<field name="product_id"/>
|
||||
|
||||
+11
-12
@@ -4,7 +4,6 @@
|
||||
import logging
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -19,15 +18,6 @@ class sale_configuration(osv.TransientModel):
|
||||
], "Product Variants",
|
||||
help='Work with product variant allows you to define some variant of the same products, an ease the product management in the ecommerce for example',
|
||||
implied_group='product.group_product_variant'),
|
||||
'module_sale_contract': fields.selection([
|
||||
(0, 'Sell based on sales order only'),
|
||||
(1, 'Activate contract management to track costs and revenues')
|
||||
], "Contracts",
|
||||
help='Allows to define your customer contracts conditions: invoicing '
|
||||
'method (fixed price, on timesheet, advance invoice), the exact pricing '
|
||||
'(650€/day for a developer), the duration (one year support contract).\n'
|
||||
'You will be able to follow the progress of the contract and invoice automatically.\n'
|
||||
'-It installs the sale_contract module.'),
|
||||
'group_sale_pricelist':fields.selection([
|
||||
(0, 'Set a fixed sale price on each product'),
|
||||
(1, 'Use pricelists to adapt your price per customers or products')
|
||||
@@ -67,10 +57,19 @@ class sale_configuration(osv.TransientModel):
|
||||
'group_sale_delivery_address': fields.selection([
|
||||
(0, "Invoicing and shipping addresses are always the same (Example: services companies)"),
|
||||
(1, 'Have 3 fields on sales orders: customer, invoice address, delivery address')
|
||||
], "Customer Addresses",
|
||||
implied_group='sale.group_delivery_invoice_address'),
|
||||
], "Customer Addresses", implied_group='sale.group_delivery_invoice_address'),
|
||||
'default_invoice_policy': fields.selection([
|
||||
('order', 'Invoice ordered quantities'),
|
||||
('delivery', 'Invoice delivered quantities'),
|
||||
('cost', 'Invoice based on costs (time and material, expenses)')
|
||||
], 'Default Invoicing', default_model='product.template')
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'default_invoice_policy': 'order',
|
||||
}
|
||||
|
||||
|
||||
def set_sale_defaults(self, cr, uid, ids, context=None):
|
||||
return {}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<field name="group_product_variant" widget="radio"/>
|
||||
<field name="module_website_sale_digital" widget="radio"/>
|
||||
<field name="group_uom" widget="radio"/>
|
||||
<field name="default_invoice_policy" widget="radio"/>
|
||||
</group>
|
||||
<group string="Quotations & Sales" id="sale">
|
||||
<field name="group_sale_pricelist" widget="radio"/>
|
||||
@@ -20,9 +21,6 @@
|
||||
<field name="module_website_quote" widget="radio"/>
|
||||
<field name="module_sale_margin" widget="radio"/>
|
||||
</group>
|
||||
<group string="Contracts" id="contract">
|
||||
<field name="module_sale_contract" widget="radio"/>
|
||||
</group>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,graph</field>
|
||||
<field name="context">{'search_default_partner_id': active_id}</field>
|
||||
<field name="context">{'search_default_partner_id': active_id, 'show_sale': True}</field>
|
||||
<field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
|
||||
+614
-1098
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp import api, fields, models
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = "sale.order.line"
|
||||
|
||||
@api.multi
|
||||
def _compute_analytic(self, domain=None):
|
||||
lines = {}
|
||||
if not domain:
|
||||
domain = [('so_line', 'in', self.ids), ('amount', '<', 0.0)]
|
||||
data = self.env['account.analytic.line'].read_group(
|
||||
domain,
|
||||
['so_line', 'unit_amount', 'product_uom_id'], ['product_uom_id', 'so_line'], lazy=False
|
||||
)
|
||||
for d in data:
|
||||
if not d['product_uom_id']:
|
||||
continue
|
||||
line = self.browse(d['so_line'][0])
|
||||
lines.setdefault(line, 0.0)
|
||||
uom = self.env['product.uom'].browse(d['product_uom_id'][0])
|
||||
|
||||
qty = self.env['product.uom']._compute_qty_obj(uom, d['unit_amount'], line.product_uom)
|
||||
lines[line] += qty
|
||||
|
||||
for line, qty in lines.items():
|
||||
line.qty_delivered = qty
|
||||
return True
|
||||
|
||||
|
||||
class AccountAnalyticLine(models.Model):
|
||||
_inherit = "account.analytic.line"
|
||||
so_line = fields.Many2one('sale.order.line', string='Sale Order Line')
|
||||
|
||||
def _get_invoice_price(self, order):
|
||||
return abs(self.amount / self.unit_amount)
|
||||
|
||||
def _get_sale_order_line_vals(self):
|
||||
order = self.env['sale.order'].search([('project_id', '=', self.account_id.id), ('state', '=', 'sale')], limit=1)
|
||||
|
||||
last_so_line = self.env['sale.order.line'].search([('order_id', '=', order.id)], order='sequence desc', limit=1)
|
||||
last_sequence = last_so_line.sequence + 1 if last_so_line else 100
|
||||
|
||||
fpos = order.fiscal_position_id or order.partner_id.property_account_position_id
|
||||
taxes = fpos.map_tax(self.product_id.taxes_id)
|
||||
price = self._get_invoice_price(order)
|
||||
|
||||
return {
|
||||
'order_id': order.id,
|
||||
'name': self.name,
|
||||
'sequence': last_sequence,
|
||||
'price_unit': price,
|
||||
'tax_id': [x.id for x in taxes],
|
||||
'discount': 0.0,
|
||||
'product_id': self.product_id.id,
|
||||
'product_uom': self.product_uom_id.id,
|
||||
'product_uom_qty': 0.0,
|
||||
'qty_delivered': self.unit_amount,
|
||||
}
|
||||
|
||||
def _get_sale_order_line(self, vals=None):
|
||||
result = dict(vals or {})
|
||||
sol = result.get('so_line', False) or self.so_line
|
||||
if not sol and self.account_id and self.product_id and self.product_id.invoice_policy in ('cost', 'order'):
|
||||
sol = self.env['sale.order.line'].search([
|
||||
('order_id.project_id', '=', self.account_id.id),
|
||||
('state', '=', 'sale'),
|
||||
('product_id', '=', self.product_id.id)],
|
||||
limit=1)
|
||||
result.update({'so_line': sol.id})
|
||||
|
||||
if not sol and self.account_id and self.product_id and self.product_id.invoice_policy == 'cost':
|
||||
order_line_vals = self._get_sale_order_line_vals()
|
||||
sol = self.env['sale.order.line'].create(order_line_vals)
|
||||
sol._compute_tax_id()
|
||||
result.update({'so_line': sol.id})
|
||||
|
||||
return result
|
||||
|
||||
@api.multi
|
||||
def write(self, values):
|
||||
if self._context.get('create', False):
|
||||
return super(AccountAnalyticLine, self).write(values)
|
||||
|
||||
todo = self.mapped('so_line')
|
||||
result = super(AccountAnalyticLine, self).write(values)
|
||||
if 'so_line' in values:
|
||||
todo |= self.mapped('so_line')
|
||||
|
||||
for line in self:
|
||||
res = self._get_sale_order_line(vals=values)
|
||||
super(AccountAnalyticLine, line).write(res)
|
||||
if 'so_line' in res:
|
||||
todo |= line.mapped('so_line')
|
||||
|
||||
todo._compute_analytic()
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def create(self, values):
|
||||
line = super(AccountAnalyticLine, self).create(values)
|
||||
res = line._get_sale_order_line(vals=values)
|
||||
line.with_context(create=True).write(res)
|
||||
line.mapped('so_line')._compute_analytic()
|
||||
return line
|
||||
@@ -25,7 +25,6 @@
|
||||
<field name="name">Laptop E5023</field>
|
||||
<field name="product_id" ref="product.product_product_25"/>
|
||||
<field name="product_uom_qty">3</field>
|
||||
<field name="product_uos_qty">3</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">2950.00</field>
|
||||
</record>
|
||||
@@ -35,7 +34,6 @@
|
||||
<field name="name">Pen drive, 16GB</field>
|
||||
<field name="product_id" ref="product.product_product_30"/>
|
||||
<field name="product_uom_qty">5</field>
|
||||
<field name="product_uos_qty">5</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">145.00</field>
|
||||
</record>
|
||||
@@ -45,7 +43,6 @@
|
||||
<field name="name">Headset USB</field>
|
||||
<field name="product_id" ref="product.product_product_33"/>
|
||||
<field name="product_uom_qty">2</field>
|
||||
<field name="product_uos_qty">2</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">65.00</field>
|
||||
</record>
|
||||
@@ -56,7 +53,6 @@
|
||||
<field name="partner_shipping_id" ref="base.res_partner_address_13"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="order_policy">manual</field>
|
||||
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||
<field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
@@ -66,7 +62,6 @@
|
||||
<field name="name">Service on demand</field>
|
||||
<field name="product_id" ref="product.product_product_1"/>
|
||||
<field name="product_uom_qty">24</field>
|
||||
<field name="product_uos_qty">24</field>
|
||||
<field name="product_uom" ref="product.product_uom_hour"/>
|
||||
<field name="price_unit">75.00</field>
|
||||
</record>
|
||||
@@ -76,7 +71,6 @@
|
||||
<field name="name">On Site Assistance</field>
|
||||
<field name="product_id" ref="product.product_product_2"/>
|
||||
<field name="product_uom_qty">30</field>
|
||||
<field name="product_uos_qty">30</field>
|
||||
<field name="product_uom" ref="product.product_uom_hour"/>
|
||||
<field name="price_unit">38.25</field>
|
||||
</record>
|
||||
@@ -87,7 +81,6 @@
|
||||
<field name="partner_shipping_id" ref="base.res_partner_4"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="order_policy">manual</field>
|
||||
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||
</record>
|
||||
|
||||
@@ -96,7 +89,6 @@
|
||||
<field name="name">On Site Monitoring</field>
|
||||
<field name="product_id" ref="product.product_product_1"/>
|
||||
<field name="product_uom_qty">10</field>
|
||||
<field name="product_uos_qty">10</field>
|
||||
<field name="product_uom" ref="product.product_uom_hour"/>
|
||||
<field name="price_unit">30.75</field>
|
||||
</record>
|
||||
@@ -106,7 +98,6 @@
|
||||
<field name="name">Toner Cartridge</field>
|
||||
<field name="product_id" ref="product.product_product_39"/>
|
||||
<field name="product_uom_qty">1</field>
|
||||
<field name="product_uos_qty">1</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">70.00</field>
|
||||
</record>
|
||||
@@ -125,7 +116,6 @@
|
||||
<field name="name">Service on demand</field>
|
||||
<field name="product_id" ref="product.product_product_1"/>
|
||||
<field name="product_uom_qty">16</field>
|
||||
<field name="product_uos_qty">16</field>
|
||||
<field name="product_uom" ref="product.product_uom_hour"/>
|
||||
<field name="price_unit">75.00</field>
|
||||
</record>
|
||||
@@ -135,7 +125,6 @@
|
||||
<field name="name">Webcam</field>
|
||||
<field name="product_id" ref="product.product_product_34"/>
|
||||
<field name="product_uom_qty">10</field>
|
||||
<field name="product_uos_qty">10</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">45.00</field>
|
||||
</record>
|
||||
@@ -145,7 +134,6 @@
|
||||
<field name="name">Multimedia Speakers</field>
|
||||
<field name="product_id" ref="product.product_product_31"/>
|
||||
<field name="product_uom_qty">3</field>
|
||||
<field name="product_uos_qty">3</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">150.00</field>
|
||||
</record>
|
||||
@@ -155,7 +143,6 @@
|
||||
<field name="name">Switch, 24 ports</field>
|
||||
<field name="product_id" ref="product.product_product_47"/>
|
||||
<field name="product_uom_qty">2</field>
|
||||
<field name="product_uos_qty">2</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">70.00</field>
|
||||
</record>
|
||||
@@ -175,7 +162,6 @@
|
||||
<field name="name">External Hard disk</field>
|
||||
<field name="product_id" ref="product.product_product_28"/>
|
||||
<field name="product_uom_qty">1</field>
|
||||
<field name="product_uos_qty">1</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">405.00</field>
|
||||
</record>
|
||||
@@ -185,7 +171,6 @@
|
||||
<field name="name">Blank DVD-RW</field>
|
||||
<field name="product_id" ref="product.product_product_36"/>
|
||||
<field name="product_uom_qty">3</field>
|
||||
<field name="product_uos_qty">3</field>
|
||||
<field name="product_uom" ref="product.product_uom_dozen"/>
|
||||
<field name="price_unit">24.00</field>
|
||||
</record>
|
||||
@@ -195,7 +180,6 @@
|
||||
<field name="name">Printer, All-in-one</field>
|
||||
<field name="product_id" ref="product.product_product_37"/>
|
||||
<field name="product_uom_qty">1</field>
|
||||
<field name="product_uos_qty">1</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">4410.00</field>
|
||||
</record>
|
||||
@@ -214,7 +198,6 @@
|
||||
<field name="name">PC Assamble + 2GB RAM</field>
|
||||
<field name="product_id" ref="product.product_product_4"/>
|
||||
<field name="product_uom_qty">1</field>
|
||||
<field name="product_uos_qty">1</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">750.00</field>
|
||||
</record>
|
||||
@@ -225,9 +208,7 @@
|
||||
<field name="partner_shipping_id" ref="base.res_partner_address_11"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="order_policy">manual</field>
|
||||
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||
<field name="date_confirm" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_line_16" model="sale.order.line">
|
||||
@@ -235,7 +216,6 @@
|
||||
<field name="name">Laptop E5023</field>
|
||||
<field name="product_id" ref="product.product_product_25"/>
|
||||
<field name="product_uom_qty">5</field>
|
||||
<field name="product_uos_qty">5</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">2950.00</field>
|
||||
</record>
|
||||
@@ -245,7 +225,6 @@
|
||||
<field name="name">GrapWorks Software</field>
|
||||
<field name="product_id" ref="product.product_product_44"/>
|
||||
<field name="product_uom_qty">1</field>
|
||||
<field name="product_uos_qty">1</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">173.00</field>
|
||||
</record>
|
||||
@@ -255,7 +234,6 @@
|
||||
<field name="name">Datacard</field>
|
||||
<field name="product_id" ref="product.product_product_46"/>
|
||||
<field name="product_uom_qty">1</field>
|
||||
<field name="product_uos_qty">1</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">40.00</field>
|
||||
</record>
|
||||
@@ -265,7 +243,6 @@
|
||||
<field name="name">USB Adapter</field>
|
||||
<field name="product_id" ref="product.product_product_48"/>
|
||||
<field name="product_uom_qty">1</field>
|
||||
<field name="product_uos_qty">1</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">18.00</field>
|
||||
</record>
|
||||
@@ -277,7 +254,6 @@
|
||||
<field name="partner_shipping_id" ref="base.res_partner_address_25"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="order_policy">manual</field>
|
||||
<field name="team_id" ref="sales_team.crm_team_1"/>
|
||||
</record>
|
||||
|
||||
@@ -286,7 +262,6 @@
|
||||
<field name="name">Laptop Customized</field>
|
||||
<field name="product_id" ref="product.product_product_27"/>
|
||||
<field name="product_uom_qty">2</field>
|
||||
<field name="product_uos_qty">2</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">3645.00</field>
|
||||
</record>
|
||||
@@ -296,7 +271,6 @@
|
||||
<field name="name">Mouse, Wireless</field>
|
||||
<field name="product_id" ref="product.product_product_12"/>
|
||||
<field name="product_uom_qty">2</field>
|
||||
<field name="product_uos_qty">2</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">12.50</field>
|
||||
</record>
|
||||
@@ -340,13 +314,14 @@ Thanks!</field>
|
||||
<!-- Demo Data for Product -->
|
||||
|
||||
<record id="advance_product_0" model="product.product">
|
||||
<field name="name">Advance</field>
|
||||
<field name="name">Deposit</field>
|
||||
<field name="categ_id" ref="product.product_category_1"/>
|
||||
<field name="type">service</field>
|
||||
<field name="list_price">150.0</field>
|
||||
<field name="invoice_policy">order</field>
|
||||
<field name="standard_price">100.0</field>
|
||||
<field name="uom_id" ref="product.product_uom_day"/>
|
||||
<field name="uom_po_id" ref="product.product_uom_day"/>
|
||||
<field name="uom_id" ref="product.product_uom_unit"/>
|
||||
<field name="uom_po_id" ref="product.product_uom_unit"/>
|
||||
<field name="company_id" eval="[]"/>
|
||||
<field name="image" type="base64" file="sale/static/img/advance_product_0-image.jpg"/>
|
||||
</record>
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<record id="product.product_product_59" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_58" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_57" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_56" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_55" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_54" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_53" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_52" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_51" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_50" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_49" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_48" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_47" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_46" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_45" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_44" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_43" model="product.product">
|
||||
<field name="invoice_policy">order</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_42" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_41" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_40" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_39" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_38" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_37" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_36" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_35" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_34" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_33" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_32" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_31" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_30" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_29" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_28" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_27" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_26" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_25" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_24" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_23" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_22" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_21" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_20" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_19" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_18" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_17" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_16" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_15" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_14" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_13" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_12" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_11b" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_11" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_10" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_9" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_8" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_7" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_6" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_5" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_5b" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_4d" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_4d" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_4c" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_4b" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_4" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_3" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_2" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_1" model="product.product">
|
||||
<field name="invoice_policy">delivery</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_1b" model="product.product">
|
||||
<field name="invoice_policy">cost</field>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_0" model="product.product">
|
||||
<field name="invoice_policy">order</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -17,7 +17,6 @@
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">450</field>
|
||||
<field name="product_uom_qty">2</field>
|
||||
<field name="product_uos_qty">2</field>
|
||||
</record>
|
||||
<record id="test_order_1_line_3" model="sale.order.line">
|
||||
<field name="order_id" ref="test_order_1"/>
|
||||
@@ -26,7 +25,6 @@
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">90</field>
|
||||
<field name="product_uom_qty">3</field>
|
||||
<field name="product_uos_qty">3</field>
|
||||
</record>
|
||||
|
||||
<assert id="test_order_1" model="sale.order" severity="error" string="The amount of the sales order is correctly computed">
|
||||
|
||||
+150
-221
@@ -44,7 +44,7 @@
|
||||
<field name="model">sale.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Sales Orders">
|
||||
<field name="partner_id" type="row"/>
|
||||
<field name="date_order" type="row"/>
|
||||
<field name="amount_total" type="measure"/>
|
||||
</pivot>
|
||||
</field>
|
||||
@@ -59,7 +59,7 @@
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="amount_total"/>
|
||||
<field name="date_confirm"/>
|
||||
<field name="date_order"/>
|
||||
<field name="state"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
@@ -74,10 +74,10 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6 text-muted">
|
||||
<span><t t-esc="record.name.value"/> <t t-esc="record.date_confirm.value"/></span>
|
||||
<span><t t-esc="record.name.value"/> <t t-esc="record.date_order.value"/></span>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<span t-attf-class="pull-right text-right label #{['draft', 'cancel'].indexOf(record.state.raw_value) > -1 ? 'label-default' : ['except_picking', 'invoice_except'].indexOf(record.state.raw_value) > -1 ? 'label-danger' : ['waiting_date', 'manual'].indexOf(record.state.raw_value) > -1 ? 'label-warning' : ['done'].indexOf(record.state.raw_value) > -1 ? 'label-success' : 'label-primary'}"><t t-esc="record.state.value"/></span>
|
||||
<span t-attf-class="pull-right text-right label #{['draft', 'cancel'].indexOf(record.state.raw_value) > -1 ? 'label-default' : ['done'].indexOf(record.state.raw_value) > -1 ? 'label-success' : 'label-primary'}"><t t-esc="record.state.value"/></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,14 +92,15 @@
|
||||
<field name="model">sale.order</field>
|
||||
<field name="priority">2</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Sales Orders" decoration-bf="message_needaction==True" decoration-muted="state=='cancel'" decoration-info="state in ('waiting_date','manual')" decoration-danger="state in ('invoice_except','shipping_except')">
|
||||
<tree string="Sales Orders" decoration-bf="message_needaction==True" decoration-muted="state=='cancel'">
|
||||
<field name="message_needaction" invisible="1"/>
|
||||
<field name="name" string="Order Number"/>
|
||||
<field name="date_order"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="user_id"/>
|
||||
<field name="amount_total" sum="Total Tax Included" widget="monetary"/>
|
||||
<field name="state"/>
|
||||
<field name="invoice_status"/>
|
||||
<field name="state" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -109,7 +110,7 @@
|
||||
<field name="model">sale.order</field>
|
||||
<field name="priority">4</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Quotation" decoration-bf="message_needaction==True" decoration-muted="state=='cancel'" decoration-info="state in ('waiting_date','manual')" decoration-danger="state in ('invoice_except','shipping_except')">
|
||||
<tree string="Quotation" decoration-bf="message_needaction==True" decoration-muted="state=='cancel'">
|
||||
<field name="message_needaction" invisible="1"/>
|
||||
<field name="name" string="Quotation Number"/>
|
||||
<field name="date_order"/>
|
||||
@@ -126,22 +127,24 @@
|
||||
<field name="model">sale.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Sales Order">
|
||||
<header>
|
||||
<button name="invoice_recreate" states="invoice_except" string="Recreate Invoice" groups="base.group_user"/>
|
||||
<button name="invoice_corrected" states="invoice_except" string="Ignore Exception" groups="base.group_user"/>
|
||||
<button name="action_button_confirm" states="sent" string="Confirm Sale" class="oe_highlight" type="object" groups="base.group_user"/>
|
||||
<button name="action_quotation_send" string="Send by Email" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="action_quotation_send" string="Send by Email" type="object" states="sent,progress,manual" groups="base.group_user"/>
|
||||
<button name="print_quotation" string="Print" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="print_quotation" string="Print" type="object" states="sent,progress,manual" groups="base.group_user"/>
|
||||
<button name="action_button_confirm" states="draft" string="Confirm Sale" type="object" groups="base.group_user"/>
|
||||
<button name="%(action_view_sale_advance_payment_inv)d" string="Create Invoice"
|
||||
type="action" states="manual" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="copy_quotation" states="cancel" string="New Copy of Quotation" type="object"/>
|
||||
<button name="cancel" states="draft,sent" string="Cancel" groups="base.group_user"/>
|
||||
<button name="action_cancel" states="manual,progress" string="Cancel Order" type="object" groups="base.group_user"/>
|
||||
<button name="invoice_cancel" states="invoice_except" string="Cancel Order" groups="base.group_user"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,sent,progress,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}'/>
|
||||
<header>
|
||||
<button name="%(action_view_sale_advance_payment_inv)d" string="Create Invoice"
|
||||
type="action" class="btn-primary"
|
||||
attrs="{'invisible': [('invoice_status', '!=', 'to invoice')]}"/>
|
||||
<button name="%(action_view_sale_advance_payment_inv)d" string="Create Invoice"
|
||||
type="action" context="{'default_advance_payment_method': 'percentage'}"
|
||||
attrs="{'invisible': ['|',('invoice_status', '!=', 'no'), ('state', '!=', 'sale')]}"/>
|
||||
<button name="action_quotation_send" string="Send by Email" type="object" states="draft" class="btn-primary"/>
|
||||
<button name="action_quotation_send" string="Send by Email" type="object" states="sent,sale"/>
|
||||
<button name="print_quotation" string="Print" type="object" states="draft" class="btn-primary"/>
|
||||
<button name="print_quotation" string="Print" type="object" states="sent,sale"/>
|
||||
<button name="action_confirm" states="sent" string="Confirm Sale" class="btn-primary" type="object" context="{'show_sale': True}"/>
|
||||
<button name="action_confirm" states="draft" string="Confirm Sale" type="object" context="{'show_sale': True}"/>
|
||||
<button name="action_cancel" states="draft,sent,sale" type="object" string="Cancel"/>
|
||||
<button name="action_draft" states="cancel" type="object" string="Set to Quotation"/>
|
||||
<button name="action_done" type="object" string="Set to Done" states="sale"
|
||||
help="If a sale order is done, you can not modify it manually anymore. But you will still be able to invoice or deliver."/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,sent,sale,done"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
@@ -149,28 +152,25 @@
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-pencil-square-o"
|
||||
attrs="{'invisible': [('invoice_count', '=', 0)]}" groups="base.group_user">
|
||||
attrs="{'invisible': [('invoice_count', '=', 0)]}">
|
||||
<field name="invoice_count" widget="statinfo" string="Invoices"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label string="Quotation" attrs="{'invisible': [('state', 'not in', ('draft', 'sent'))]}"/>
|
||||
<label string="Sales Order" attrs="{'invisible': [('state', 'in', ('draft', 'sent'))]}"/>
|
||||
<h1>
|
||||
<field name="name" class="oe_inline" readonly="1"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id" on_change="onchange_partner_id(partner_id, context)" domain="[('customer','=',True)]" context="{'search_default_customer':1, 'show_address': 1}" options='{"always_reload": True}'/>
|
||||
<field name="partner_id" domain="[('customer','=',True)]" context="{'search_default_customer':1, 'show_address': 1}" options='{"always_reload": True}'/>
|
||||
<field name="partner_invoice_id" groups="sale.group_delivery_invoice_address" context="{'default_type':'invoice'}"/>
|
||||
<field name="partner_shipping_id" on_change="onchange_delivery_id(company_id, partner_id, partner_shipping_id, fiscal_position_id)" groups="sale.group_delivery_invoice_address" context="{'default_type':'delivery'}"/>
|
||||
<field name="project_id" context="{'partner_id':partner_invoice_id, 'manager_id': user_id, 'default_pricelist_id':pricelist_id, 'default_name':name, 'default_type': 'contract'}" groups="sale.group_analytic_accounting" domain="[('type','in',['view','normal','contract'])]"/>
|
||||
<field name="contract_state" groups="sale.group_analytic_accounting" widget="label_selection" attrs="{'invisible': [('project_id', '=', False)]}" options="{'classes': {'template': 'default', 'draft': 'info', 'open': 'success', 'pending': 'warning', 'close': 'danger', 'cancelled': 'warning'}}" readonly="1"/>
|
||||
<field name="partner_shipping_id" groups="sale.group_delivery_invoice_address" context="{'default_type':'delivery'}"/>
|
||||
<field name="project_id" context="{'default_partner_id':partner_invoice_id, 'default_name':name}"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date_order"/>
|
||||
<field domain="[('type','=','sale')]" name="pricelist_id" groups="product.group_sale_pricelist" on_change="onchange_pricelist_id(pricelist_id,order_line)"/>
|
||||
<field name="date_order" invisible="1"/>
|
||||
<field domain="[('type','=','sale')]" name="pricelist_id" groups="product.group_sale_pricelist"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="validity_date"/>
|
||||
<field name="payment_term_id" options="{'no_create': True}"/>
|
||||
@@ -178,31 +178,35 @@
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Order Lines">
|
||||
<field name="order_line" mode="tree,kanban">
|
||||
<field name="order_line" mode="tree,kanban"
|
||||
attrs="{'readonly': [('state', 'in', ('done','cancel'))]}">
|
||||
<form string="Sales Order Lines">
|
||||
<header groups="base.group_user">
|
||||
<button name="%(action_view_sale_order_line_make_invoice)d" states="confirmed" string="Invoice" type="action" icon="terp-document-new"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,done" statusbar_colors='{"exception":"red","cancel":"red"}'/>
|
||||
</header>
|
||||
<group>
|
||||
<group>
|
||||
<field name="product_id"
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
|
||||
groups="base.group_user"
|
||||
on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, False, product_uos_qty, False, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position_id, False, context)"/>
|
||||
<label for="product_uom_qty"/>
|
||||
attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"
|
||||
/>
|
||||
<field name="invoice_status" invisible="1"/>
|
||||
<field name="qty_to_invoice" invisible="1"/>
|
||||
<field name="qty_delivered_updateable" invisible="1"/>
|
||||
<label for="product_uom_qty" string="Ordered Quantity"/>
|
||||
<div>
|
||||
<field
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom, 'uom_qty_change':True}"
|
||||
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, False, parent.fiscal_position_id, True, context)"/>
|
||||
name="product_uom_qty" class="oe_inline"/>
|
||||
<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)"/>
|
||||
attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"/>
|
||||
</div>
|
||||
<label for="product_uos_qty" groups="product.group_uos"/>
|
||||
<div groups="product.group_uos">
|
||||
<field name="product_uos_qty" class="oe_inline"/>
|
||||
<field name="product_uos" options='{"no_open": True}' class="oe_inline"/>
|
||||
<label for="qty_delivered" string="Delivered Quantity"
|
||||
invisible="not context.get('show_sale')"/>
|
||||
<div>
|
||||
<field name="qty_delivered" invisible="not context.get('show_sale')"
|
||||
attrs="{'readonly': [('qty_delivered_updateable', '=', False)]}"/>
|
||||
</div>
|
||||
<label for="qty_invoiced" string="Invoiced Quantity" invisible="not context.get('show_sale')"/>
|
||||
<div>
|
||||
<field name="qty_invoiced" invisible="not context.get('show_sale')"/>
|
||||
</div>
|
||||
<field name="price_unit"/>
|
||||
<label for="discount" groups="sale.group_discount_per_so_line"/>
|
||||
@@ -212,7 +216,10 @@
|
||||
</group>
|
||||
<group>
|
||||
<field name="tax_id" widget="many2many_tags" domain="[('type_tax_use','=','sale'),('company_id','=',parent.company_id)]"/>
|
||||
<field name="th_weight"/>
|
||||
<label for="customer_lead"/>
|
||||
<div>
|
||||
<field name="customer_lead" class="oe_inline"/> days
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<label for="name"/>
|
||||
@@ -221,41 +228,43 @@
|
||||
<label for="invoice_lines"/>
|
||||
<field name="invoice_lines"/>
|
||||
</div>
|
||||
</form>
|
||||
<tree string="Sales Order Lines" editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="th_weight" invisible="1"/>
|
||||
</form>
|
||||
<tree string="Sales Order Lines" editable="bottom" decoration-info="invoice_status=='to invoice'">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="product_id"
|
||||
attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
|
||||
groups="base.group_user"
|
||||
on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, False, product_uos_qty, False, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position_id, False, context)"/>
|
||||
/>
|
||||
<field name="name"/>
|
||||
<field name="product_uom_qty"
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_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_id, True, context)"/>
|
||||
string="Ordered Qty"
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"/>
|
||||
<field name="qty_delivered" invisible="not context.get('show_sale')"
|
||||
attrs="{'readonly': [('qty_delivered_updateable', '=', False)]}"/>
|
||||
<field name="qty_invoiced"
|
||||
invisible="not context.get('show_sale')"/>
|
||||
<field name="qty_to_invoice" invisible="1"/>
|
||||
<field name="product_uom"
|
||||
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)"
|
||||
attrs="{'readonly': [('state', 'in', ('sale','done', 'cancel'))]}"
|
||||
groups="product.group_uom" options='{"no_open": True}'/>
|
||||
<field name="product_uos_qty" groups="product.group_uos" invisible="1"/>
|
||||
<field name="product_uos" string="UoS" groups="product.group_uos" invisible="1"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="price_unit"
|
||||
attrs="{'readonly': [('qty_invoiced', '>', 0)]}"/>
|
||||
<field name="tax_id" widget="many2many_tags" domain="[('type_tax_use','=','sale'),('company_id','=',parent.company_id)]"/>
|
||||
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
||||
<field name="price_subtotal" widget="monetary"/>
|
||||
<field name="qty_delivered_updateable" invisible="1"/>
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="invoice_status" invisible="1"/>
|
||||
</tree>
|
||||
<kanban class="o_kanban_mobile">
|
||||
<field name="product_id"
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
|
||||
groups="base.group_user"
|
||||
on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, False, product_uos_qty, False, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position_id, False, context)"/>
|
||||
/>
|
||||
<field name="product_uom_qty"
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_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_id, True, context)"/>
|
||||
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"/>
|
||||
<field name="product_uom"
|
||||
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)"
|
||||
groups="product.group_uom" options='{"no_open": True}'/>
|
||||
<field name="product_uos_qty" groups="product.group_uos" invisible="1"/>
|
||||
<field name="price_subtotal"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
@@ -283,36 +292,36 @@
|
||||
<field name="amount_tax" widget='monetary' options="{'currency_field': 'currency_id'}"/>
|
||||
<div class="oe_subtotal_footer_separator oe_inline">
|
||||
<label for="amount_total" />
|
||||
<button name="button_dummy"
|
||||
states="draft,sent" string="(update)" type="object" class="oe_edit_only oe_link"/>
|
||||
<!-- <button name="button_dummy"
|
||||
states="draft,sent" string="(update)" type="object" class="oe_edit_only oe_link"/> -->
|
||||
</div>
|
||||
<field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator" widget='monetary' options="{'currency_field': 'currency_id'}"/>
|
||||
</group>
|
||||
<field name="note" class="oe_inline" placeholder="An administrator can set up default Terms and conditions in your Company settings."/>
|
||||
<field name="note" class="oe_inline" placeholder="Setup default terms and conditions in your company settings."/>
|
||||
<div class="oe_clear"/>
|
||||
</page>
|
||||
<page string="Other Information" groups="base.group_user">
|
||||
<page string="Other Information">
|
||||
<group>
|
||||
<group string="Sales Information" name="sales_person" groups="base.group_user">
|
||||
<group string="Sales Information" name="sales_person">
|
||||
<field name="user_id" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'account.group_account_invoice', 'base.group_sale_salesman_all_leads']}"/>
|
||||
<field name="team_id" options="{'no_create': True}"/>
|
||||
<field name="client_order_ref"/>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group name="sale_pay" string="Invoicing">
|
||||
<field name="fiscal_position_id" options="{'no_create': True}"
|
||||
on_change="onchange_fiscal_position(fiscal_position_id, order_line, context)"/>
|
||||
<field name="fiscal_position_id" options="{'no_create': True}"/>
|
||||
<field name="invoice_status"
|
||||
attrs="{'invisible': [('state', 'not in', ('sale','done'))]}"/>
|
||||
</group>
|
||||
<group string="Reporting" name="technical" groups="base.group_no_one">
|
||||
<field groups="base.group_no_one" name="origin"/>
|
||||
<field groups="base.group_no_one" name="invoiced"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
@@ -344,12 +353,13 @@
|
||||
<field name="team_id" string="Sales Team"/>
|
||||
<field name="project_id"/>
|
||||
<field name="product_id"/>
|
||||
<filter string="My" domain="[('user_id','=',uid)]" name="my_sale_orders_filter"/>
|
||||
<filter string="My Orders" domain="[('user_id','=',uid)]" name="my_sale_orders_filter"/>
|
||||
<separator/>
|
||||
<filter string="Quotations" name="draft" domain="[('state','in',('draft','sent'))]" help="Sales Order that haven't yet been confirmed"/>
|
||||
<filter string="Sales" name="sales" domain="[('state','in',('manual','progress'))]"/>
|
||||
<filter string="To Invoice" domain="[('state','=','manual')]" help="Sales Order ready to be invoiced"/>
|
||||
<filter string="Done" domain="[('state','=','done')]" help="Sales Order done"/>
|
||||
<filter string="Sales" name="sales" domain="[('state','in',('progress','Done'))]"/>
|
||||
<separator/>
|
||||
<filter string="To Invoice" domain="[('invoice_status','=','to invoice')]"/>
|
||||
<filter string="Upselling" domain="[('invoice_status','=','upselling')]"/>
|
||||
<separator/>
|
||||
<filter string="Important Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
@@ -368,7 +378,7 @@
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,kanban,form,calendar,pivot,graph</field>
|
||||
<field name="search_view_id" ref="view_sales_order_filter"/>
|
||||
<field name="context">{}</field>
|
||||
<field name="context">{'show_sale': True}</field>
|
||||
<field name="domain">[('state', 'not in', ('draft', 'sent', 'cancel'))]</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
@@ -381,28 +391,37 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_orders" id="menu_sale_order" parent="base.menu_sales" sequence="12" groups="base.group_sale_salesman,base.group_sale_manager"/>
|
||||
<menuitem action="action_orders"
|
||||
id="menu_sale_order" parent="base.menu_sales"
|
||||
sequence="12" groups="base.group_sale_salesman,base.group_sale_manager"/>
|
||||
|
||||
<record id="action_orders_exception" model="ir.actions.act_window">
|
||||
<field name="name">Sales in Exception</field>
|
||||
<record id="action_orders_to_invoice" model="ir.actions.act_window">
|
||||
<field name="name">Sales to Invoice</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,calendar,graph</field>
|
||||
<field name="domain">[('state','in',('shipping_except','invoice_except'))]</field>
|
||||
<field name="filter" eval="True"/>
|
||||
<field name="search_view_id" ref="view_sales_order_filter"/>
|
||||
<field name="view_mode">tree,form,calendar,graph,pivot</field>
|
||||
<field name="domain">[('invoice_status','=','to invoice')]</field>
|
||||
</record>
|
||||
<menuitem name="Invoicing"
|
||||
id="menu_sale_invoicing" parent="base.menu_base_partner"
|
||||
sequence="5"/>
|
||||
<menuitem action="action_orders_to_invoice"
|
||||
id="menu_sale_order_invoice" parent="sale.menu_sale_invoicing"
|
||||
sequence="2"/>
|
||||
|
||||
<record id="action_orders_in_progress" model="ir.actions.act_window">
|
||||
<field name="name">Sales Order in Progress</field>
|
||||
<record id="action_orders_upselling" model="ir.actions.act_window">
|
||||
<field name="name">Upselling Opportunities</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,calendar,graph</field>
|
||||
<field name="domain">[('state','in',('progress','waiting_date','manual'))]</field>
|
||||
<field name="search_view_id" ref="view_sales_order_filter"/>
|
||||
<field name="view_mode">tree,form,calendar,graph,pivot</field>
|
||||
<field name="domain">[('invoice_status','=','upselling')]</field>
|
||||
<field name="context">{'show_sale': True}</field>
|
||||
</record>
|
||||
<menuitem action="action_orders_upselling"
|
||||
id="menu_sale_order_upselling" parent="sale.menu_sale_invoicing"
|
||||
sequence="5"/>
|
||||
|
||||
|
||||
<record id="action_quotations" model="ir.actions.act_window">
|
||||
@@ -433,18 +452,6 @@
|
||||
action="action_quotations" parent="base.menu_sales"
|
||||
sequence="11"/>
|
||||
|
||||
<record id="action_order_tree" model="ir.actions.act_window">
|
||||
<field name="name">Old Quotations</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,calendar,graph</field>
|
||||
<field name="domain">[('state','=','draft'),('date_order','<',time.strftime('%Y-%m-%d %H:%M:%S'))]</field>
|
||||
<field name="filter" eval="True"/>
|
||||
<field name="search_view_id" ref="view_sales_order_filter"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="view_order_line_tree" model="ir.ui.view">
|
||||
<field name="name">sale.order.line.tree</field>
|
||||
<field name="model">sale.order.line</field>
|
||||
@@ -454,63 +461,23 @@
|
||||
<field name="order_id"/>
|
||||
<field name="order_partner_id"/>
|
||||
<field name="name"/>
|
||||
<field name="product_uom_qty" string="Qty"/>
|
||||
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
|
||||
<field name="salesman_id"/>
|
||||
<field name="product_uom_qty" string="Qty"/>
|
||||
<field name="qty_delivered"/>
|
||||
<field name="qty_invoiced"/>
|
||||
<field name="qty_to_invoice"/>
|
||||
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
|
||||
<field name="price_subtotal" sum="Total" widget="monetary"/>
|
||||
<field name="state"/>
|
||||
<field name="invoiced"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_order_line_form2" model="ir.ui.view">
|
||||
<field name="name">sale.order.line.form2</field>
|
||||
<field name="model">sale.order.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Sales Order Lines" create="false">
|
||||
<header>
|
||||
<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" groups="base.group_user"/>
|
||||
<button name="button_cancel" string="Cancel Line" type="object" states="confirmed,exception" groups="base.group_user"/>
|
||||
<button name="button_done" string="Done" type="object" attrs="{'invisible': ['|',('invoiced', '=', 0), ('state', 'not in', ('confirmed', 'exception'))]}" class="oe_highlight" groups="base.group_user"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,done" statusbar_colors='{"exception":"red","cancel":"red"}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
<label for="order_id" class="oe_edit_only"/>
|
||||
<h1><field name="order_id" domain="[('state','!=','done')]"/></h1>
|
||||
<label for="order_partner_id" class="oe_edit_only"/>
|
||||
<h2><field name="order_partner_id"/></h2>
|
||||
<group>
|
||||
<group>
|
||||
<field name="product_id"/>
|
||||
<label for="product_uom_qty"/>
|
||||
<div>
|
||||
<field name="product_uom_qty" readonly="1" class="oe_inline"/>
|
||||
<field name="product_uom" groups="product.group_uom" class="oe_inline"/>
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<field name="price_unit"/>
|
||||
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
||||
<field name="price_subtotal" widget="monetary"/>
|
||||
<field name="invoiced"/>
|
||||
<field name="company_id" groups="base.group_multi_company" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<label for="name"/>
|
||||
<field name="name"/>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_sales_order_line_filter" model="ir.ui.view">
|
||||
<field name="name">sale.order.line.select</field>
|
||||
<field name="model">sale.order.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Sales Order">
|
||||
<filter string="To Invoice" domain="[('invoiced','<>', 1),('state','=','done')]" help="Sales Order Lines ready to be invoiced"/>
|
||||
<separator/>
|
||||
<filter string="Confirmed" domain="[('state', 'in', ['confirmed', 'done'])]" name="confirmed"/>
|
||||
<filter string="To Invoice" domain="[('qty_to_invoice','<>', 0)]" help="Sales Order Lines ready to be invoiced"/>
|
||||
<separator/>
|
||||
<filter string="My Sales Order Lines" domain="[('salesman_id','=',uid)]" help="Sales Order Lines related to a Sales Order of mine"/>
|
||||
<field name="order_id"/>
|
||||
@@ -521,77 +488,40 @@
|
||||
<filter string="Product" domain="[]" context="{'group_by':'product_id'}"/>
|
||||
<filter string="Order" domain="[]" context="{'group_by':'order_id'}"/>
|
||||
<filter string="Salesperson" domain="[]" context="{'group_by':'salesman_id'}"/>
|
||||
<filter string="Status" domain="[]" context="{'group_by':'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_sales_order_uninvoiced_line_filter" model="ir.ui.view">
|
||||
<field name="name">sale.order.uninvoiced.line</field>
|
||||
<field name="model">sale.order.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Uninvoiced Lines">
|
||||
<filter string="To Do" domain="[('state','=','confirmed')]" name="sale order" help="Confirmed sales order lines, not yet delivered"/>
|
||||
<filter string="Done" domain="[('state','=','done')]" name="sale_order_done" help="Sales order lines done"/>
|
||||
<filter string="Shipped" domain="[('state','=','done')]" name="unshipped" help="Sales Order Lines that are in 'done' state"/>
|
||||
<separator/>
|
||||
<filter string="Uninvoiced" name="uninvoiced" domain="[('invoiced','<>', 1),('state','<>','draft'),('state','<>','cancel')]" help="Sales Order Lines that are confirmed, done or in exception state and haven't yet been invoiced"/>
|
||||
<separator/>
|
||||
<filter string="My Sales Order Lines" domain="[('salesman_id','=',uid)]" help="My Sales Order Lines"/>
|
||||
<field name="order_id"/>
|
||||
<field name="order_partner_id" operator="child_of"/>
|
||||
<field name="product_id"/>
|
||||
<field name="salesman_id"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Order" domain="[]" context="{'group_by':'order_id'}" help="Order reference"/>
|
||||
<filter string="Product" domain="[]" context="{'group_by':'product_id'}"/>
|
||||
<filter string="Status" domain="[]" context="{'group_by':'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
<record id="action_product_sale_list" model="ir.actions.act_window">
|
||||
<field name="name">Sale Order Lines</field>
|
||||
<field name="res_model">sale.order.line</field>
|
||||
<field name="context">{'search_default_product_id': [active_id], 'default_product_id': active_id}</field>
|
||||
<field name="domain">[('state', 'in', ['sale', 'done'])]</field>
|
||||
</record>
|
||||
|
||||
<record id="action_order_line_tree2" model="ir.actions.act_window">
|
||||
<field name="name">Order Lines to Invoice</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">sale.order.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="search_view_id" ref="view_sales_order_uninvoiced_line_filter"/>
|
||||
<field name="context">{"search_default_uninvoiced":1}</field>
|
||||
<field name="filter" eval="True"/>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
Here is a list of each sales order line to be invoiced. You can
|
||||
invoice sales orders partially, by lines of sales order. You do
|
||||
not need this list if you invoice from the delivery orders or
|
||||
if you invoice sales totally.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_order_line_tree3" model="ir.actions.act_window">
|
||||
<field name="name">Uninvoiced and Delivered Lines</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">sale.order.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('invoiced','<>', 1),('state','=','done')]</field>
|
||||
<field name="filter" eval="True"/>
|
||||
</record>
|
||||
<record id="action_order_line_product_tree" model="ir.actions.act_window">
|
||||
<field name="context">{}</field><!-- force empty -->
|
||||
<field name="name">Sales Order Lines</field>
|
||||
<field name="res_model">sale.order.line</field>
|
||||
<field name="view_id" ref="view_order_line_tree"/>
|
||||
<field name="context">{'search_default_confirmed': 1}</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="product_form_view_sale_order_button">
|
||||
<field name="name">product.product.sale.order</field>
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_normal_form_view"/>
|
||||
<field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/>
|
||||
<field name="arch" type="xml">
|
||||
<div name="button_box" position="inside">
|
||||
<button class="oe_stat_button" name="%(action_product_sale_list)d"
|
||||
type="action" icon="fa-usd"
|
||||
groups="base.group_no_one">
|
||||
<field string="Sales" name="sales_count" widget="statinfo" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_template_form_view_sale_order_button">
|
||||
<field name="name">product.template.sale.order.button</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_only_form_view"/>
|
||||
<field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/>
|
||||
<field name="arch" type="xml">
|
||||
<div name="button_box" position="inside">
|
||||
<button class="oe_stat_button" name="action_view_sales"
|
||||
@@ -603,23 +533,20 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_template_form_view_sale_order_button">
|
||||
<field name="name">product.template.sale.order.button</field>
|
||||
<record model="ir.ui.view" id="product_template_form_view_invoice_policy">
|
||||
<field name="name">product.template.invoice.policy</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_only_form_view"/>
|
||||
<field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<div name="button_box" position="inside">
|
||||
<button class="oe_stat_button" name="action_view_sales"
|
||||
type="object" icon="fa-usd"
|
||||
groups="base.group_no_one">
|
||||
<field string="Sales" name="sales_count" widget="statinfo" />
|
||||
</button>
|
||||
</div>
|
||||
<field name="type" position="after">
|
||||
<field name="invoice_policy" widget="radio"/>
|
||||
<field name="track_service" widget="radio" invisible="True"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_company_inherit_form2">
|
||||
<field name="name">res.company.form.inherit</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
@@ -672,6 +599,7 @@
|
||||
<field name="search_view_id" ref="sale.view_sales_order_filter"/>
|
||||
<field name="domain">[('state','not in',('draft','sent','cancel'))]</field>
|
||||
<field name="context">{
|
||||
'show_sale': True,
|
||||
'search_default_team_id': [active_id],
|
||||
'default_team_id': active_id,
|
||||
}
|
||||
@@ -693,8 +621,9 @@
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,calendar,graph</field>
|
||||
<field name="search_view_id" ref="sale.view_sales_order_filter"/>
|
||||
<field name="domain">[('state', '=', 'manual')]</field>
|
||||
<field name="domain">[('state', '=', 'sale'),('invoice_status','=','to invoice')]</field>
|
||||
<field name="context">{
|
||||
'show_sale': True,
|
||||
'search_default_team_id': [active_id],
|
||||
'default_team_id': active_id,
|
||||
}
|
||||
|
||||
@@ -1,303 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="wkf_sale" model="workflow">
|
||||
<field name="name">sale.order.basic</field>
|
||||
<field name="osv">sale.order</field>
|
||||
<field name="on_create">True</field>
|
||||
</record>
|
||||
|
||||
<!-- Activity -->
|
||||
<record id="act_draft" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<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 id="act_router" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<field name="name">router</field>
|
||||
<field name="kind">function</field>
|
||||
<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_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">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>
|
||||
<field name="flow_stop">True</field>
|
||||
<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>
|
||||
<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>
|
||||
<field name="kind">subflow</field>
|
||||
<field name="subflow_id" search="[('name','=','account.invoice.basic')]"/>
|
||||
<field name="action">action_invoice_create()</field>
|
||||
</record>
|
||||
<record id="act_invoice_except" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<field name="name">invoice_except</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">action_invoice_cancel()</field>
|
||||
</record>
|
||||
<record id="act_invoice_end" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<field name="name">invoice_end</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">action_invoice_end()</field>
|
||||
</record>
|
||||
<record id="act_invoice_cancel" model="workflow.activity">
|
||||
<field name="wkf_id" ref="wkf_sale"/>
|
||||
<field name="name">invoice_cancel</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="kind">stopall</field>
|
||||
<field name="action">action_cancel()</field>
|
||||
</record>
|
||||
|
||||
<!-- Transistion -->
|
||||
|
||||
<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_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_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_invoice_invoice_manual" model="workflow.transition">
|
||||
<field name="act_from" ref="act_wait_invoice"/>
|
||||
<field name="act_to" ref="act_invoice"/>
|
||||
<field name="signal">manual_invoice</field>
|
||||
</record>
|
||||
<record id="trans_invoice_invoice_end" model="workflow.transition">
|
||||
<field name="act_from" ref="act_invoice"/>
|
||||
<field name="act_to" ref="act_invoice_end"/>
|
||||
<field name="signal">subflow.paid</field>
|
||||
</record>
|
||||
<record id="trans_invoice_invoice_except" model="workflow.transition">
|
||||
<field name="act_from" ref="act_invoice"/>
|
||||
<field name="act_to" ref="act_invoice_except"/>
|
||||
<field name="signal">subflow.cancel</field>
|
||||
</record>
|
||||
<record id="trans_invoice_except_invoice" model="workflow.transition">
|
||||
<field name="act_from" ref="act_invoice_except"/>
|
||||
<field name="act_to" ref="act_invoice"/>
|
||||
<field name="signal">invoice_recreate</field>
|
||||
</record>
|
||||
<record id="trans_invoice_except_invoice_end" model="workflow.transition">
|
||||
<field name="act_from" ref="act_invoice_except"/>
|
||||
<field name="act_to" ref="act_invoice_end"/>
|
||||
<field name="signal">invoice_corrected</field>
|
||||
</record>
|
||||
<record id="trans_invoice_except_invoice_cancel" model="workflow.transition">
|
||||
<field name="act_from" ref="act_invoice_except"/>
|
||||
<field name="act_to" ref="act_invoice_cancel"/>
|
||||
<field name="signal">invoice_cancel</field>
|
||||
</record>
|
||||
<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>
|
||||
|
||||
<!--
|
||||
Procurements creation and checking branch
|
||||
-->
|
||||
|
||||
<!-- Activity -->
|
||||
|
||||
<record id="act_wait_ship" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">wait_ship</field>
|
||||
</record>
|
||||
|
||||
<record id="act_cancel3" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">cancel3</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="kind">stopall</field>
|
||||
<field name="action">action_cancel()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_ship" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">ship</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">action_ship_create()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_ship_ignore" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">ship_ignore</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">action_ignore_delivery_exception()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_ship_end" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">ship_end</field>
|
||||
<field name="kind">dummy</field>
|
||||
</record>
|
||||
|
||||
<record id="act_ship_cancel" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">ship_cancel</field>
|
||||
<field name="flow_stop">True</field>
|
||||
<field name="kind">stopall</field>
|
||||
<field name="action">action_cancel()</field>
|
||||
</record>
|
||||
|
||||
<record id="act_ship_except" model="workflow.activity">
|
||||
<field name="wkf_id" ref="sale.wkf_sale"/>
|
||||
<field name="name">ship_except</field>
|
||||
<field name="kind">function</field>
|
||||
<field name="action">write({'state':'shipping_except'})</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<!-- Transition -->
|
||||
|
||||
<record id="trans_router_wait_ship" model="workflow.transition">
|
||||
<field name="act_from" ref="sale.act_router"/>
|
||||
<field name="act_to" ref="act_wait_ship"/>
|
||||
</record>
|
||||
|
||||
<record id="trans_router_wait_invoice_shipping" model="workflow.transition">
|
||||
<field name="act_from" ref="sale.act_wait_invoice"/>
|
||||
<field name="act_to" ref="sale.act_invoice_end"/>
|
||||
<field name="condition">(order_policy=='picking')</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_wait_invoice_invoice" model="workflow.transition">
|
||||
<field name="act_from" ref="sale.act_wait_invoice"/>
|
||||
<field name="act_to" ref="sale.act_invoice"/>
|
||||
<field name="condition">order_policy=='prepaid'</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">procurement_needed() and ((order_policy!='prepaid') or invoiced)</field>
|
||||
</record>
|
||||
<record id="trans_wait_ship_done" model="workflow.transition">
|
||||
<field name="act_from" ref="act_wait_ship"/>
|
||||
<field name="act_to" ref="act_ship_end"/>
|
||||
<field name="condition">not procurement_needed()</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_ship_end_done" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship_end"/>
|
||||
<field name="act_to" ref="sale.act_done"/>
|
||||
</record>
|
||||
|
||||
<record id="trans_ship_ship_end" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship"/>
|
||||
<field name="act_to" ref="act_ship_end"/>
|
||||
<field name="signal">ship_end</field>
|
||||
<field name="trigger_model" eval="False"/> <!-- Force empty -->
|
||||
<field name="trigger_expr_id" eval="False"/> <!-- Force empty -->
|
||||
<field name="condition" eval="True"/> <!-- Force empty -->
|
||||
</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="signal">ship_except</field>
|
||||
<field name="condition" eval="True"/> <!-- Force empty -->
|
||||
</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_ignore" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship_except"/>
|
||||
<field name="act_to" ref="act_ship_ignore"/>
|
||||
<field name="signal">ship_corrected</field>
|
||||
</record>
|
||||
|
||||
<record id="trans_ship_ignore_ship_end" model="workflow.transition">
|
||||
<field name="act_from" ref="act_ship_ignore"/>
|
||||
<field name="act_to" ref="act_ship_end"/>
|
||||
</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>
|
||||
@@ -1,82 +0,0 @@
|
||||
-
|
||||
Salesman can also cancel order therefore test with that user which have salesman rights,
|
||||
-
|
||||
!context
|
||||
uid: 'res_users_salesman'
|
||||
-
|
||||
In order to test the cancel sale order.
|
||||
I confirm order (with at least 2 lines)
|
||||
-
|
||||
!workflow {model: sale.order, action: order_confirm, ref: sale_order_8}
|
||||
|
||||
-
|
||||
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'
|
||||
-
|
||||
I check that Invoice should not be created.
|
||||
-
|
||||
!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."
|
||||
-
|
||||
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, 'product_id': ref("sale_order_line_21")})
|
||||
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}: |
|
||||
invoice_ids = self.browse(cr, uid, ref("sale_order_8")).invoice_ids
|
||||
for invoice in invoice_ids:
|
||||
invoice.signal_workflow('invoice_cancel')
|
||||
-
|
||||
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.
|
||||
-
|
||||
!workflow {model: sale.order, action: invoice_recreate, ref: sale_order_8}
|
||||
-
|
||||
I check that the invoice is correctly created with all lines.
|
||||
-
|
||||
!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_ids)
|
||||
assert total_order_line == 2, "wrong number of invoice lines, got %s" % total_order_line
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
-
|
||||
I create a draft Sale Order with 2 lines but 1 canceled in order to check if the canceled lines are not considered in the logic
|
||||
-
|
||||
!record {model: sale.order, id: sale_order_cl_2}:
|
||||
partner_id: base.res_partner_3
|
||||
partner_invoice_id: base.res_partner_address_25
|
||||
partner_shipping_id: base.res_partner_address_25
|
||||
pricelist_id: product.list0
|
||||
order_policy: manual
|
||||
-
|
||||
!record {model: sale.order.line, id: sale_order_cl_2_line_1}:
|
||||
order_id: sale_order_cl_2
|
||||
product_id: product.product_product_27
|
||||
product_uom_qty: 1
|
||||
product_uom: 1
|
||||
price_unit: 3645
|
||||
name: 'Laptop Customized'
|
||||
-
|
||||
!record {model: sale.order.line, id: sale_order_cl_2_line_2}:
|
||||
order_id: sale_order_cl_2
|
||||
product_id: product.product_product_12
|
||||
product_uom_qty: 1
|
||||
product_uom: 1
|
||||
price_unit: 12.50
|
||||
name: 'Mouse, Wireless'
|
||||
-
|
||||
I cancel the first line
|
||||
-
|
||||
!python {model: sale.order.line, id: sale_order_cl_2_line_1}: |
|
||||
self.button_cancel()
|
||||
-
|
||||
I confirm the sale order
|
||||
-
|
||||
!workflow {model: sale.order, action: order_confirm, ref: sale_order_cl_2}
|
||||
-
|
||||
Invoice the whole sale order
|
||||
-
|
||||
!python {model: sale.advance.payment.inv}: |
|
||||
ctx = context.copy()
|
||||
ctx.update({"active_model": 'sale.order',
|
||||
"active_ids": [ref("sale_order_cl_2")],
|
||||
"active_id":ref("sale_order_cl_2")})
|
||||
pay_id = self.create(cr, uid, {'advance_payment_method': 'all'})
|
||||
self.create_invoices(cr, uid, [pay_id], context=ctx)
|
||||
-
|
||||
I check the invoice
|
||||
-
|
||||
!python {model: sale.order, id: sale_order_cl_2}: |
|
||||
invoice = self.invoice_ids
|
||||
assert len(invoice.invoice_line_ids) == 1, "Only 1 line should be invoiced because the other one is canceled, got %d" % len(invoice.invoice_line_ids)
|
||||
-
|
||||
I set the sale to done
|
||||
-
|
||||
!python {model: sale.order, id: sale_order_cl_2}: |
|
||||
self.action_done()
|
||||
-
|
||||
And check if the canceled line is still canceled
|
||||
-
|
||||
!assert {model: sale.order.line, id: sale_order_cl_2_line_1, string: The canceled line should still be canceled}:
|
||||
- state == 'cancel'
|
||||
@@ -1,28 +0,0 @@
|
||||
-
|
||||
Create a user as 'Salesmanager'
|
||||
-
|
||||
!record {model: res.users, id: res_users_salesmanager, view: False}:
|
||||
company_id: base.main_company
|
||||
name: Sales manager
|
||||
login: sm
|
||||
email: salesmanager@yourcompany.com
|
||||
-
|
||||
I added groups for Salesmanager.
|
||||
-
|
||||
!record {model: res.users, id: res_users_salesmanager}:
|
||||
groups_id:
|
||||
- base.group_sale_manager
|
||||
-
|
||||
Create a user as 'Salesman'
|
||||
-
|
||||
!record {model: res.users, id: res_users_salesman, view: False}:
|
||||
company_id: base.main_company
|
||||
name: Salesman
|
||||
login: su
|
||||
email: salesman@yourcompany.com
|
||||
-
|
||||
I added groups for Salesman.
|
||||
-
|
||||
!record {model: res.users, id: res_users_salesman}:
|
||||
groups_id:
|
||||
- base.group_sale_salesman_all_leads
|
||||
@@ -1,19 +0,0 @@
|
||||
-
|
||||
Sales manager can only delete order therefore test with that user which have sales manager rights,
|
||||
-
|
||||
!context
|
||||
uid: 'res_users_salesmanager'
|
||||
-
|
||||
I try to delete In progress order and check Error Message.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
try:
|
||||
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_7'))
|
||||
self.unlink(cr, uid, [id])
|
||||
@@ -1,66 +0,0 @@
|
||||
-
|
||||
I confirm the Quotation with "On Demand" order policy.
|
||||
-
|
||||
!workflow {model: sale.order, action: order_confirm, ref: sale_order_2}
|
||||
-
|
||||
I check that Invoice should not created.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
sale_order = self.browse(cr, uid, ref("sale_order_2"))
|
||||
assert len(sale_order.invoice_ids) == 0, "Invoice should not created."
|
||||
-
|
||||
I create advance invoice where type is 'Fixed Price'.
|
||||
-
|
||||
!python {model: sale.advance.payment.inv}: |
|
||||
ctx = context.copy()
|
||||
ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale_order_2")], "active_id":ref("sale_order_2")})
|
||||
order_line = self.pool.get('sale.order.line').browse(cr, uid, ref("sale_order_line_4"), context=context)
|
||||
pay_id = self.create(cr, uid, {'advance_payment_method': 'fixed', 'product_id': order_line.product_id.id, 'amount': order_line.price_unit})
|
||||
self.create_invoices(cr, uid, [pay_id], context=ctx)
|
||||
-
|
||||
I check Invoice which made advance.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
order = self.browse(cr, uid, ref('sale_order_2'))
|
||||
assert order.invoice_ids, "Invoice should be created after make advance invoice."
|
||||
-
|
||||
I create advance invoice where type is 'Invoice all the Sale Order'.
|
||||
-
|
||||
!python {model: sale.advance.payment.inv}: |
|
||||
ctx = context.copy()
|
||||
ctx.update({"active_model": 'sale.order', "active_ids": [ref("sale_order_2")], "active_id":ref("sale_order_2")})
|
||||
pay_id = self.create(cr, uid, {'advance_payment_method': 'all'})
|
||||
self.create_invoices(cr, uid, [pay_id], context=ctx)
|
||||
-
|
||||
I check Invoice which made advance where type is 'Invoice all the Sale Order'.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
order = self.browse(cr, uid, ref('sale_order_2'))
|
||||
assert order.invoice_ids, "Invoice should be created after make advance invoice where type is 'Invoice all the Sale Order'."
|
||||
-
|
||||
I open the Invoice.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
so = self.browse(cr, uid, ref("sale_order_2"))
|
||||
account_invoice_obj = self.pool.get('account.invoice')
|
||||
for invoice in so.invoice_ids:
|
||||
invoice.signal_workflow('invoice_open')
|
||||
-
|
||||
I pay the invoice.
|
||||
-
|
||||
!python {model: account.invoice}: |
|
||||
sale_order = self.pool.get('sale.order')
|
||||
order = sale_order.browse(cr, uid, ref("sale_order_2"))
|
||||
journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'cash'), ('company_id', '=', order.company_id.id)], limit=1)
|
||||
import time
|
||||
for invoice in order.invoice_ids:
|
||||
self.pay_and_reconcile(cr, uid, [invoice.id], journal_ids[0])
|
||||
-
|
||||
I check Invoice after do manual.
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
sale_order = self.browse(cr, uid, ref("sale_order_2"))
|
||||
assert sale_order.invoice_ids, "Invoice should be created."
|
||||
assert sale_order.invoice_count, "Order is not invoiced."
|
||||
assert sale_order.invoiced, "Order is not paid."
|
||||
assert sale_order.state == 'done', 'Order should be Done.'
|
||||
@@ -1,46 +0,0 @@
|
||||
-
|
||||
Test the data with salesman,
|
||||
-
|
||||
!context
|
||||
uid: 'res_users_salesman'
|
||||
-
|
||||
In order to test process of the Sale Order, I create sale order
|
||||
-
|
||||
!record {model: sale.order, id: sale_order_test1}:
|
||||
partner_id: base.res_partner_2
|
||||
note: Invoice after delivery
|
||||
payment_term_id: account.account_payment_term
|
||||
order_line:
|
||||
- product_id: product.product_product_7
|
||||
product_uom_qty: 8
|
||||
-
|
||||
I verify that the onchange was correctly triggered
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
from openerp.tools import float_compare
|
||||
order_line = self.browse(cr, uid, ref('sale.sale_order_test1')).order_line
|
||||
assert order_line[0].name == u'[A8767] Apple In-Ear Headphones', "The onchange function of product was not correctly triggered"
|
||||
assert float_compare(order_line[0].price_unit, 79.0, precision_digits=2) == 0, "The onchange function of product was not correctly triggered"
|
||||
assert order_line[0].product_uom_qty == 8, "The onchange function of product was not correctly triggered"
|
||||
assert order_line[0].product_uom.id == ref('product.product_uom_unit'), "The onchange function of product was not correctly triggered"
|
||||
|
||||
-
|
||||
I create another sale order
|
||||
-
|
||||
!record {model: sale.order, id: sale_order_test2}:
|
||||
partner_id: base.res_partner_2
|
||||
order_line:
|
||||
- product_id: product.product_product_7
|
||||
product_uom_qty: 16
|
||||
product_uom: product.product_uom_dozen
|
||||
-
|
||||
I verify that the onchange was correctly triggered
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
from openerp.tools import float_compare
|
||||
order_line = self.browse(cr, uid, ref('sale.sale_order_test2')).order_line
|
||||
assert order_line[0].name == u'[A8767] Apple In-Ear Headphones', "The onchange function of product was not correctly triggered"
|
||||
assert float_compare(order_line[0].price_unit, 79.0 * 12, precision_digits=2) == 0, "The onchange function of product was not correctly triggered"
|
||||
assert order_line[0].product_uom.id == ref('product.product_uom_dozen'), "The onchange function of product was not correctly triggered"
|
||||
assert order_line[0].product_uom_qty == 16, "The onchange function of product was not correctly triggered"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_sale_to_invoice
|
||||
import test_sale_to_invoice
|
||||
import test_sale_order
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from openerp.tests import common
|
||||
|
||||
|
||||
class TestSale(common.TransactionCase):
|
||||
def setUp(self):
|
||||
super(TestSale, self).setUp()
|
||||
# some users
|
||||
group_manager = self.env.ref('base.group_sale_manager')
|
||||
group_user = self.env.ref('base.group_sale_salesman')
|
||||
self.manager = self.env['res.users'].create({
|
||||
'name': 'Andrew Manager',
|
||||
'login': 'manager',
|
||||
'alias_name': 'andrew',
|
||||
'email': 'a.m@example.com',
|
||||
'signature': '--\nAndreww',
|
||||
'notify_email': 'always',
|
||||
'groups_id': [(6, 0, [group_manager.id])]
|
||||
})
|
||||
self.user = self.env['res.users'].create({
|
||||
'name': 'Mark User',
|
||||
'login': 'user',
|
||||
'alias_name': 'mark',
|
||||
'email': 'm.u@example.com',
|
||||
'signature': '--\nMark',
|
||||
'notify_email': 'always',
|
||||
'groups_id': [(6, 0, [group_user.id])]
|
||||
})
|
||||
# create quotation with differend kinds of products (all possible combinations)
|
||||
self.products = {
|
||||
'prod_order': self.env.ref('product.product_product_43'),
|
||||
'prod_del': self.env.ref('product.product_product_47'),
|
||||
'serv_order': self.env.ref('product.product_product_0'),
|
||||
'serv_del': self.env.ref('product.product_product_56'),
|
||||
}
|
||||
|
||||
self.partner = self.env.ref('base.res_partner_1')
|
||||
@@ -0,0 +1,124 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from openerp.exceptions import UserError, AccessError
|
||||
|
||||
from test_sale_common import TestSale
|
||||
|
||||
|
||||
class TestSaleOrder(TestSale):
|
||||
def test_sale_order(self):
|
||||
""" Test the sale order flow (invoicing and quantity updates)
|
||||
- Invoice repeatedly while varrying delivered quantities and check that invoice are always what we expect
|
||||
"""
|
||||
# DBO TODO: validate invoice and register payments
|
||||
inv_obj = self.env['account.invoice']
|
||||
so = self.env['sale.order'].create({
|
||||
'partner_id': self.partner.id,
|
||||
'partner_invoice_id': self.partner.id,
|
||||
'partner_shipping_id': self.partner.id,
|
||||
'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in self.products.iteritems()],
|
||||
'pricelist_id': self.env.ref('product.list0').id,
|
||||
})
|
||||
self.assertEqual(so.amount_total, sum([2 * p.list_price for (k, p) in self.products.iteritems()]), 'Sale: total amount is wrong')
|
||||
|
||||
# send quotation
|
||||
so.force_quotation_send()
|
||||
self.assertTrue(so.state == 'sent', 'Sale: state after sending is wrong')
|
||||
|
||||
# confirm quotation
|
||||
so.action_confirm()
|
||||
self.assertTrue(so.state == 'sale')
|
||||
self.assertTrue(so.invoice_status == 'to invoice')
|
||||
|
||||
# create invoice: only 'invoice on order' products are invoiced
|
||||
inv_id = so.action_invoice_create()
|
||||
inv = inv_obj.browse(inv_id)
|
||||
self.assertEqual(len(inv.invoice_line_ids), 2, 'Sale: invoice is missing lines')
|
||||
self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'order' else 0 for (k, p) in self.products.iteritems()]), 'Sale: invoice total amount is wrong')
|
||||
self.assertTrue(so.invoice_status == 'no', 'Sale: SO status after invoicing should be "nothing to invoice"')
|
||||
self.assertTrue(len(so.invoice_ids) == 1, 'Sale: invoice is missing')
|
||||
|
||||
# deliver lines except 'time and material' then invoice again
|
||||
for line in so.order_line:
|
||||
line.qty_delivered = 2 if line.product_id.invoice_policy in ['order', 'delivery'] else 0
|
||||
self.assertTrue(so.invoice_status == 'to invoice', 'Sale: SO status after delivery should be "to invoice"')
|
||||
inv_id = so.action_invoice_create()
|
||||
inv = inv_obj.browse(inv_id)
|
||||
self.assertEqual(len(inv.invoice_line_ids), 2, 'Sale: second invoice is missing lines')
|
||||
self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'delivery' else 0 for (k, p) in self.products.iteritems()]), 'Sale: second invoice total amount is wrong')
|
||||
self.assertTrue(so.invoice_status == 'invoiced', 'Sale: SO status after invoicing everything should be "invoiced"')
|
||||
self.assertTrue(len(so.invoice_ids) == 2, 'Sale: invoice is missing')
|
||||
# go over the sold quantity
|
||||
for line in so.order_line:
|
||||
if line.product_id == self.products['serv_order']:
|
||||
line.qty_delivered = 10
|
||||
self.assertTrue(so.invoice_status == 'upselling', 'Sale: SO status after increasing delivered qty higher than ordered qty should be "upselling"')
|
||||
|
||||
# upsell and invoice
|
||||
for line in so.order_line:
|
||||
if line.product_id == self.products['serv_order']:
|
||||
line.product_uom_qty = 10
|
||||
inv_id = so.action_invoice_create()
|
||||
inv = inv_obj.browse(inv_id)
|
||||
self.assertEqual(len(inv.invoice_line_ids), 1, 'Sale: third invoice is missing lines')
|
||||
self.assertEqual(inv.amount_total, 8 * self.products['serv_order'].list_price, 'Sale: second invoice total amount is wrong')
|
||||
self.assertTrue(so.invoice_status == 'invoiced', 'Sale: SO status after invoicing everything (including the upsel) should be "invoiced"')
|
||||
|
||||
def test_unlink_cancel(self):
|
||||
""" Test deleting and cancelling sale orders depending on their state and on the user's rights """
|
||||
so = self.env['sale.order'].create({
|
||||
'partner_id': self.partner.id,
|
||||
'partner_invoice_id': self.partner.id,
|
||||
'partner_shipping_id': self.partner.id,
|
||||
'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in self.products.iteritems()],
|
||||
'pricelist_id': self.env.ref('product.list0').id,
|
||||
})
|
||||
# only quotations are deletable
|
||||
with self.assertRaises(UserError):
|
||||
so.action_confirm()
|
||||
so.unlink()
|
||||
so_copy = so.copy()
|
||||
with self.assertRaises(AccessError):
|
||||
so_copy.sudo(self.user).unlink()
|
||||
self.assertTrue(so_copy.sudo(self.manager).unlink(), 'Sale: deleting a quotation should be possible')
|
||||
|
||||
# cancelling and setting to done, you should not be able to delete any SO ever
|
||||
so.action_cancel()
|
||||
self.assertTrue(so.state == 'cancel', 'Sale: cancelling SO should always be possible')
|
||||
with self.assertRaises(UserError):
|
||||
so.sudo(self.manager).unlink()
|
||||
so.action_done()
|
||||
self.assertTrue(so.state == 'done', 'Sale: SO not done')
|
||||
|
||||
def test_cost_invoicing(self):
|
||||
""" Test confirming a vendor invoice to reinvoice cost on the so """
|
||||
serv_cost = self.env.ref('product.product_product_1b')
|
||||
prod_gap = self.env.ref('product.product_product_1')
|
||||
so = self.env['sale.order'].create({
|
||||
'partner_id': self.partner.id,
|
||||
'partner_invoice_id': self.partner.id,
|
||||
'partner_shipping_id': self.partner.id,
|
||||
'order_line': [(0, 0, {'name': prod_gap.name, 'product_id': prod_gap.id, 'product_uom_qty': 2, 'product_uom': prod_gap.uom_id.id, 'price_unit': prod_gap.list_price})],
|
||||
'pricelist_id': self.env.ref('product.list0').id,
|
||||
})
|
||||
so.action_confirm()
|
||||
so._create_analytic_account()
|
||||
inv_partner = self.env.ref('base.res_partner_2')
|
||||
company = self.env.ref('base.main_company')
|
||||
journal = self.env['account.journal'].create({'name': 'Purchase Journal - Test', 'code': 'STPJ', 'type': 'purchase', 'company_id': company.id})
|
||||
account_payable = self.env['account.account'].create({'code': 'X1111', 'name': 'Sale - Test Payable Account', 'user_type_id': self.env.ref('account.data_account_type_payable').id, 'reconcile': True})
|
||||
account_income = self.env['account.account'].create({'code': 'X1112', 'name': 'Sale - Test Account', 'user_type_id': self.env.ref('account.data_account_type_direct_costs').id})
|
||||
invoice_vals = {
|
||||
'name': '',
|
||||
'type': 'in_invoice',
|
||||
'partner_id': inv_partner.id,
|
||||
'invoice_line_ids': [(0, 0, {'name': serv_cost.name, 'product_id': serv_cost.id, 'quantity': 2, 'uom_id': serv_cost.uom_id.id, 'price_unit': serv_cost.standard_price, 'account_analytic_id': so.project_id.id, 'account_id': account_income.id})],
|
||||
'account_id': account_payable.id,
|
||||
'journal_id': journal.id,
|
||||
'currency_id': company.currency_id.id,
|
||||
}
|
||||
inv = self.env['account.invoice'].create(invoice_vals)
|
||||
inv.signal_workflow('invoice_open')
|
||||
sol = so.order_line.filtered(lambda l: l.product_id == serv_cost)
|
||||
self.assertTrue(sol, 'Sale: cost invoicing does not add lines when confirming vendor invoice')
|
||||
self.assertTrue(sol.price_unit == 160 and sol.qty_delivered == 2 and sol.product_uom_qty == sol.qty_invoiced == 0, 'Sale: line is wrong after confirming vendor invoice')
|
||||
@@ -20,7 +20,6 @@ class TestSale(TestMail):
|
||||
account_obj = self.env['account.account']
|
||||
# Usefull record id
|
||||
group_id = IrModelData.xmlid_to_res_id('account.group_account_invoice') or False
|
||||
product_id = IrModelData.xmlid_to_res_id('product.product_category_3') or False
|
||||
company_id = IrModelData.xmlid_to_res_id('base.main_company') or False
|
||||
|
||||
# Usefull accounts
|
||||
@@ -53,13 +52,13 @@ class TestSale(TestMail):
|
||||
# In order to test I create sale order and confirmed it.
|
||||
order = self.env['sale.order'].create({
|
||||
'partner_id': partner.id,
|
||||
'date_order': datetime.today()})
|
||||
order_line = self.env['sale.order.line'].create({
|
||||
'order_id': order.id,
|
||||
'product_id': product_id})
|
||||
'partner_invoice_id': partner.id,
|
||||
'partner_shipping_id': partner.id,
|
||||
'date_order': datetime.today(),
|
||||
'pricelist_id': self.env.ref('product.list0').id})
|
||||
assert order, "Sale order will not created."
|
||||
context = {"active_model": 'sale.order', "active_ids": [order.id], "active_id": order.id}
|
||||
order.with_context(context).action_button_confirm()
|
||||
order.with_context(context).action_confirm()
|
||||
# Now I create invoice.
|
||||
payment = self.env['sale.advance.payment.inv'].create({'advance_payment_method': 'fixed', 'amount': 5})
|
||||
invoice = payment.with_context(context).create_invoices()
|
||||
|
||||
@@ -67,28 +67,30 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="sale_tbody">
|
||||
<tr t-foreach="doc.order_line" t-as="l">
|
||||
<td>
|
||||
<span t-field="l.name"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.product_uom_qty"/>
|
||||
<span groups="product.group_uom" t-field="l.product_uom"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_unit"/>
|
||||
</td>
|
||||
<td t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
|
||||
<span t-field="l.discount"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="', '.join(map(lambda x: x.name, l.tax_id))"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_subtotal"
|
||||
t-field-options='{"widget": "monetary", "display_currency": "doc.pricelist_id.currency_id"}'/>
|
||||
</td>
|
||||
</tr>
|
||||
<t t-foreach="doc.order_line" t-as="l">
|
||||
<tr t-if="l.product_uom_qty">
|
||||
<td>
|
||||
<span t-field="l.name"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.product_uom_qty"/>
|
||||
<span groups="product.group_uom" t-field="l.product_uom"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_unit"/>
|
||||
</td>
|
||||
<td t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">
|
||||
<span t-field="l.discount"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="', '.join(map(lambda x: x.name, l.tax_id))"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_subtotal"
|
||||
t-field-options='{"widget": "monetary", "display_currency": "doc.pricelist_id.currency_id"}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import sale_make_invoice
|
||||
import sale_line_invoice
|
||||
import sale_make_invoice_advance
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.tools.translate import _
|
||||
from openerp import workflow
|
||||
from openerp.exceptions import UserError
|
||||
|
||||
class sale_order_line_make_invoice(osv.osv_memory):
|
||||
_name = "sale.order.line.make.invoice"
|
||||
_description = "Sale OrderLine Make_invoice"
|
||||
|
||||
def _prepare_invoice(self, cr, uid, order, lines, context=None):
|
||||
a = order.partner_id.property_account_receivable_id.id
|
||||
if order.partner_id and order.partner_id.property_payment_term_id.id:
|
||||
pay_term = order.partner_id.property_payment_term_id.id
|
||||
else:
|
||||
pay_term = False
|
||||
return {
|
||||
'name': order.client_order_ref or '',
|
||||
'origin': order.name,
|
||||
'type': 'out_invoice',
|
||||
'reference': "P%dSO%d" % (order.partner_id.id, order.id),
|
||||
'account_id': a,
|
||||
'partner_id': order.partner_invoice_id.id,
|
||||
'invoice_line_ids': [(6, 0, lines)],
|
||||
'currency_id' : order.pricelist_id.currency_id.id,
|
||||
'comment': order.note,
|
||||
'payment_term_id': pay_term,
|
||||
'fiscal_position_id': order.fiscal_position_id.id or order.partner_id.property_account_position_id.id,
|
||||
'user_id': order.user_id and order.user_id.id or False,
|
||||
'company_id': order.company_id and order.company_id.id or False,
|
||||
'date_invoice': fields.date.today(),
|
||||
'team_id': order.team_id.id,
|
||||
}
|
||||
|
||||
|
||||
def make_invoices(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
To make invoices.
|
||||
|
||||
@param self: The object pointer.
|
||||
@param cr: A database cursor
|
||||
@param uid: ID of the user currently logged in
|
||||
@param ids: the ID or list of IDs
|
||||
@param context: A standard dictionary
|
||||
|
||||
@return: A dictionary which of fields with values.
|
||||
|
||||
"""
|
||||
if context is None: context = {}
|
||||
res = False
|
||||
invoices = {}
|
||||
|
||||
#TODO: merge with sale.py/make_invoice
|
||||
def make_invoice(order, lines):
|
||||
"""
|
||||
To make invoices.
|
||||
|
||||
@param order:
|
||||
@param lines:
|
||||
|
||||
@return:
|
||||
|
||||
"""
|
||||
inv = self._prepare_invoice(cr, uid, order, lines)
|
||||
inv_id = self.pool.get('account.invoice').create(cr, uid, inv)
|
||||
return inv_id
|
||||
|
||||
sales_order_line_obj = self.pool.get('sale.order.line')
|
||||
sales_order_obj = self.pool.get('sale.order')
|
||||
for line in sales_order_line_obj.browse(cr, uid, context.get('active_ids', []), context=context):
|
||||
if (not line.invoiced) and (line.state not in ('draft', 'cancel')):
|
||||
if not line.order_id in invoices:
|
||||
invoices[line.order_id] = []
|
||||
line_id = sales_order_line_obj.invoice_line_create(cr, uid, [line.id])
|
||||
for lid in line_id:
|
||||
invoices[line.order_id].append(lid)
|
||||
for order, il in invoices.items():
|
||||
res = make_invoice(order, il)
|
||||
cr.execute('INSERT INTO sale_order_invoice_rel \
|
||||
(order_id,invoice_id) values (%s,%s)', (order.id, res))
|
||||
sales_order_obj.invalidate_cache(cr, uid, ['invoice_ids'], [order.id], context=context)
|
||||
flag = True
|
||||
sales_order_obj.message_post(cr, uid, [order.id], body=_("Invoice created"), context=context)
|
||||
data_sale = sales_order_obj.browse(cr, uid, order.id, context=context)
|
||||
for line in data_sale.order_line:
|
||||
if not line.invoiced and line.state != 'cancel':
|
||||
flag = False
|
||||
break
|
||||
if flag:
|
||||
line.order_id.write({'state': 'progress'})
|
||||
workflow.trg_validate(uid, 'sale.order', order.id, 'all_lines', cr)
|
||||
|
||||
if not invoices:
|
||||
raise UserError(_('Invoice cannot be created for this Sales Order Line due to one of the following reasons:\n1.The state of this sales order line is either "draft" or "cancel"!\n2.The Sales Order Line is Invoiced!'))
|
||||
if context.get('open_invoices', False):
|
||||
return self.open_invoices(cr, uid, ids, res, context=context)
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
def open_invoices(self, cr, uid, ids, invoice_ids, context=None):
|
||||
""" open a view on one of the given invoice_ids """
|
||||
ir_model_data = self.pool.get('ir.model.data')
|
||||
form_res = ir_model_data.get_object_reference(cr, uid, 'account', 'invoice_form')
|
||||
form_id = form_res and form_res[1] or False
|
||||
tree_res = ir_model_data.get_object_reference(cr, uid, 'account', 'invoice_tree')
|
||||
tree_id = tree_res and tree_res[1] or False
|
||||
|
||||
return {
|
||||
'name': _('Invoice'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form,tree',
|
||||
'res_model': 'account.invoice',
|
||||
'res_id': invoice_ids,
|
||||
'view_id': False,
|
||||
'views': [(form_id, 'form'), (tree_id, 'tree')],
|
||||
'context': {'type': 'out_invoice'},
|
||||
'type': 'ir.actions.act_window',
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="view_sale_order_line_make_invoice" model="ir.ui.view">
|
||||
<field name="name">Sales OrderLine Make Invoice</field>
|
||||
<field name="model">sale.order.line.make.invoice</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Create invoices">
|
||||
<p class="oe_grey">
|
||||
All items in these order lines will be invoiced. You can also invoice a percentage of the sales order
|
||||
or a fixed price (for advances) directly from the sales order form if you prefer.
|
||||
</p>
|
||||
<footer>
|
||||
<button name="make_invoices" string="Create & View Invoice" type="object"
|
||||
context="{'open_invoices': True}" class="oe_highlight"/>
|
||||
<button name="make_invoices" string="Create Invoices" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-default" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_view_sale_order_line_make_invoice" model="ir.actions.act_window">
|
||||
<field name="name">Create Invoice</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">sale.order.line.make.invoice</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_sale_order_line_make_invoice"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.values" id="sale_order_line_make_invoice">
|
||||
<field name="model_id" ref="sale.model_sale_order_line" />
|
||||
<field name="name">Make Invoices</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_view_sale_order_line_make_invoice'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model">sale.order.line</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -1,52 +0,0 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
from openerp.exceptions import UserError
|
||||
|
||||
class sale_make_invoice(osv.osv_memory):
|
||||
_name = "sale.make.invoice"
|
||||
_description = "Sales Make Invoice"
|
||||
_columns = {
|
||||
'grouped': fields.boolean('Group the invoices', help='Check the box to group the invoices for the same customers'),
|
||||
'invoice_date': fields.date('Invoice Date'),
|
||||
}
|
||||
_defaults = {
|
||||
'grouped': False,
|
||||
'invoice_date': fields.date.context_today,
|
||||
}
|
||||
|
||||
def view_init(self, cr, uid, fields_list, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
record_id = context and context.get('active_id', False)
|
||||
order = self.pool.get('sale.order').browse(cr, uid, record_id, context=context)
|
||||
if order.state == 'draft':
|
||||
raise UserError(_('You cannot create invoice when sales order is not confirmed.'))
|
||||
return False
|
||||
|
||||
def make_invoices(self, cr, uid, ids, context=None):
|
||||
order_obj = self.pool.get('sale.order')
|
||||
mod_obj = self.pool.get('ir.model.data')
|
||||
act_obj = self.pool.get('ir.actions.act_window')
|
||||
newinv = []
|
||||
if context is None:
|
||||
context = {}
|
||||
data = self.read(cr, uid, ids)[0]
|
||||
for sale_order in order_obj.browse(cr, uid, context.get(('active_ids'), []), context=context):
|
||||
if sale_order.state != 'manual':
|
||||
raise UserError(_("You shouldn't manually invoice the following sale order %s") % (sale_order.name))
|
||||
|
||||
order_obj.action_invoice_create(cr, uid, context.get(('active_ids'), []), data['grouped'], date_invoice=data['invoice_date'])
|
||||
orders = order_obj.browse(cr, uid, context.get(('active_ids'), []), context=context)
|
||||
for o in orders:
|
||||
for i in o.invoice_ids:
|
||||
newinv.append(i.id)
|
||||
# Dummy call to workflow, will not create another invoice but bind the new invoice to the subflow
|
||||
order_obj.signal_workflow(cr, uid, [o.id for o in orders if o.order_policy == 'manual'], 'manual_invoice')
|
||||
result = mod_obj.get_object_reference(cr, uid, 'account', 'action_invoice_tree1')
|
||||
id = result and result[1] or False
|
||||
result = act_obj.read(cr, uid, [id], context=context)[0]
|
||||
result['domain'] = "[('id','in', [" + ','.join(map(str, newinv)) + "])]"
|
||||
|
||||
return result
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="view_sale_order_make_invoice" model="ir.ui.view">
|
||||
<field name="name">Create invoices</field>
|
||||
<field name="model">sale.make.invoice</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Create invoices">
|
||||
<separator colspan="4" string="Do you really want to create the invoice(s)?" />
|
||||
<group>
|
||||
<field name="grouped"/>
|
||||
<field name="invoice_date"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="make_invoices" string="Create Invoices" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-default" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_sale_order_make_invoice" model="ir.actions.act_window">
|
||||
<field name="name">Make Invoices</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">sale.make.invoice</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_sale_order_make_invoice"/>
|
||||
<field name="target">new</field>
|
||||
<field name="multi">True</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.values" id="sale_order_make_invoice">
|
||||
<field name="model_id" ref="sale.model_sale_order" />
|
||||
<field name="name">Make Invoices</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_sale_order_make_invoice'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model">sale.order</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -1,197 +1,135 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
import time
|
||||
|
||||
from openerp import api, fields, models, _
|
||||
import openerp.addons.decimal_precision as dp
|
||||
from openerp.exceptions import UserError
|
||||
|
||||
class sale_advance_payment_inv(osv.osv_memory):
|
||||
class SaleAdvancePaymentInv(models.TransientModel):
|
||||
_name = "sale.advance.payment.inv"
|
||||
_description = "Sales Advance Payment Invoice"
|
||||
|
||||
_columns = {
|
||||
'advance_payment_method':fields.selection(
|
||||
[('all', 'Invoice the whole sales order'), ('percentage','Percentage'), ('fixed','Fixed price (deposit)'),
|
||||
('lines', 'Some order lines')],
|
||||
'What do you want to invoice?', required=True,
|
||||
help="""Use Invoice the whole sale order to create the final invoice.\nUse Percentage to invoice a percentage of the total amount.\nUse Fixed Price to invoice a specific amount in advance.\nUse Some Order Lines to invoice a selection of the sales order lines."""),
|
||||
'qtty': fields.float('Quantity', digits=(16, 2), required=True),
|
||||
'product_id': fields.many2one('product.product', 'Advance Product',
|
||||
domain=[('type', '=', 'service')],
|
||||
help="Select a product of type service which is called 'Advance Product'.\nYou may have to create it and set it as a default value on this field."),
|
||||
'amount': fields.float('Advance Amount', digits=0,
|
||||
help="The amount to be invoiced in advance. \nTaxes are not taken into account for advance invoices."),
|
||||
}
|
||||
@api.model
|
||||
def _count(self):
|
||||
return len(self._context.get('active_ids', []))
|
||||
|
||||
def _get_advance_product(self, cr, uid, context=None):
|
||||
@api.model
|
||||
def _get_advance_payment_method(self):
|
||||
if self._count() == 1:
|
||||
sale_obj = self.env['sale.order']
|
||||
order = sale_obj.browse(self._context.get('active_ids'))[0]
|
||||
if all([line.product_id.invoice_policy == 'order' for line in order.order_line]):
|
||||
return 'all'
|
||||
return 'delivered'
|
||||
|
||||
@api.model
|
||||
def _get_advance_product(self):
|
||||
try:
|
||||
product = self.pool.get('ir.model.data').get_object(cr, uid, 'sale', 'advance_product_0')
|
||||
return self.env['ir.model.data'].xmlid_to_res_id('sale.advance_product_0', raise_if_not_found=True)
|
||||
except ValueError:
|
||||
# a ValueError is returned if the xml id given is not found in the table ir_model_data
|
||||
return False
|
||||
return product.id
|
||||
|
||||
_defaults = {
|
||||
'advance_payment_method': 'all',
|
||||
'qtty': 1.0,
|
||||
'product_id': _get_advance_product,
|
||||
}
|
||||
advance_payment_method = fields.Selection([
|
||||
('delivered', 'Ready to invoice'),
|
||||
('all', 'Ready to invoice (with refunds)'),
|
||||
('percentage', 'Deposit (percentage)'),
|
||||
('fixed', 'Deposit (fixed amount)')
|
||||
], string='What do you want to invoice?', default=_get_advance_payment_method, required=True)
|
||||
product_id = fields.Many2one('product.product', string='Deposit Product', domain=[('type', '=', 'service')], default=_get_advance_product)
|
||||
count = fields.Integer(default=_count, string='# of Orders')
|
||||
amount = fields.Float('Deposit Amount', digits=dp.get_precision('Account'), help="The amount to be invoiced in advance, taxes excluded.")
|
||||
|
||||
def _translate_advance(self, cr, uid, percentage=False, context=None):
|
||||
return _("Advance of %s %%") if percentage else _("Advance of %s %s")
|
||||
@api.onchange('advance_payment_method')
|
||||
def onchange_advance_payment_method(self):
|
||||
if self.advance_payment_method == 'percentage':
|
||||
return {'value': {'amount':0, 'product_id':False}}
|
||||
return {}
|
||||
|
||||
def onchange_method(self, cr, uid, ids, advance_payment_method, product_id, context=None):
|
||||
if advance_payment_method == 'percentage':
|
||||
return {'value': {'amount':0, 'product_id':False }}
|
||||
if product_id:
|
||||
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
|
||||
return {'value': {'amount': product.list_price}}
|
||||
return {'value': {'amount': 0}}
|
||||
@api.multi
|
||||
def _create_invoice(self, order, so_line, amount):
|
||||
inv_obj = self.env['account.invoice']
|
||||
ir_property_obj = self.env['ir.property']
|
||||
|
||||
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')
|
||||
invoice_obj = self.pool.get('account.invoice')
|
||||
wizard = self.browse(cr, uid, ids[0], context)
|
||||
sale_ids = context.get('active_ids', [])
|
||||
account_id = False
|
||||
if self.product_id.id:
|
||||
account_id = self.product_id.property_account_income_id.id
|
||||
if not account_id:
|
||||
prop = ir_property_obj.get('property_account_income_categ_id', 'product.category')
|
||||
prop_id = prop and prop.id or False
|
||||
account_id = order.fiscal_position_id.map_account(prop_id)
|
||||
if not account_id:
|
||||
raise UserError(
|
||||
_('There is no income account defined for this product: "%s". You may have to install a chart of account from Accounting app, settings menu.') % \
|
||||
(self.product_id.name,))
|
||||
|
||||
result = []
|
||||
for sale in sale_obj.browse(cr, uid, sale_ids, context=context):
|
||||
new_invoice = invoice_obj.new(cr, uid, {
|
||||
'invoice_line_ids':[(0, 0, {'product_id': wizard.product_id.id})],
|
||||
'partner_id': sale.partner_id.id,
|
||||
'fiscal_position_id': sale.fiscal_position_id.id,
|
||||
'type': 'out_invoice',
|
||||
})
|
||||
inv_line = new_invoice.invoice_line_ids[0]
|
||||
inv_line.invoice_id = new_invoice #Little hack to in order to old <-> new api
|
||||
inv_line._onchange_product_id()
|
||||
if self.amount <= 0.00:
|
||||
raise UserError(_('The value of Advance Amount must be positive.'))
|
||||
if self.advance_payment_method == 'percentage':
|
||||
amount = order.amount_untaxed * self.amount / 100
|
||||
name = _("Advance of %s%%") % (self.amount,)
|
||||
else:
|
||||
amount = self.amount
|
||||
name = _('Advance')
|
||||
|
||||
res = inv_line._convert_to_write(inv_line._cache)
|
||||
invoice = inv_obj.create({
|
||||
'name': order.client_order_ref or order.name,
|
||||
'origin': order.name,
|
||||
'type': 'out_invoice',
|
||||
'reference': False,
|
||||
'account_id': order.partner_id.property_account_receivable_id.id,
|
||||
'partner_id': order.partner_invoice_id.id,
|
||||
'invoice_line_ids': [(0, 0, {
|
||||
'name': name,
|
||||
'origin': order.name,
|
||||
'account_id': account_id,
|
||||
'price_unit': amount,
|
||||
'quantity': 1.0,
|
||||
'discount': 0.0,
|
||||
'uom_id': self.product_id.uom_id.id,
|
||||
'product_id': self.product_id.id,
|
||||
'sale_line_ids': [(6, 0, [so_line.id])],
|
||||
'invoice_line_tax_ids': [(6, 0, [x.id for x in self.product_id.taxes_id])],
|
||||
'account_analytic_id': order.project_id.id or False,
|
||||
})],
|
||||
'currency_id': order.pricelist_id.currency_id.id,
|
||||
'payment_term_id': order.payment_term_id.id,
|
||||
'fiscal_position_id': order.fiscal_position_id.id or order.partner_id.property_account_position_id.id,
|
||||
'team_id': order.team_id.id,
|
||||
})
|
||||
invoice.compute_taxes()
|
||||
return invoice
|
||||
|
||||
# determine and check income account
|
||||
if not wizard.product_id.id :
|
||||
prop = ir_property_obj.get(cr, uid,
|
||||
'property_account_income_categ_id', 'product.category', context=context)
|
||||
prop_id = prop and prop.id or False
|
||||
account_id = fiscal_obj.map_account(cr, uid, sale.fiscal_position_id or False, prop_id)
|
||||
if not account_id:
|
||||
raise UserError(
|
||||
_('There is no income account defined as global property.'))
|
||||
res['account_id'] = account_id
|
||||
if not res.get('account_id'):
|
||||
raise UserError(
|
||||
_('There is no income account defined for this product: "%s" (id:%d).') % \
|
||||
(wizard.product_id.name, wizard.product_id.id,))
|
||||
@api.multi
|
||||
def create_invoices(self):
|
||||
sale_orders = self.env['sale.order'].browse(self._context.get('active_ids', []))
|
||||
|
||||
# determine invoice amount
|
||||
if wizard.amount <= 0.00:
|
||||
raise UserError(_('The value of Advance Amount must be positive.'))
|
||||
if wizard.advance_payment_method == 'percentage':
|
||||
inv_amount = sale.amount_untaxed * wizard.amount / 100
|
||||
if not res.get('name'):
|
||||
res['name'] = self._translate_advance(cr, uid, percentage=True, context=dict(context, lang=sale.partner_id.lang)) % (wizard.amount)
|
||||
else:
|
||||
inv_amount = wizard.amount
|
||||
if not res.get('name'):
|
||||
#TODO: should find a way to call formatLang() from rml_parse
|
||||
symbol = sale.pricelist_id.currency_id.symbol
|
||||
if sale.pricelist_id.currency_id.position == 'after':
|
||||
symbol_order = (inv_amount, symbol)
|
||||
else:
|
||||
symbol_order = (symbol, inv_amount)
|
||||
res['name'] = self._translate_advance(cr, uid, context=dict(context, lang=sale.partner_id.lang)) % symbol_order
|
||||
|
||||
# create the invoice
|
||||
inv_line_values = {
|
||||
'name': res.get('name'),
|
||||
'origin': sale.name,
|
||||
'account_id': res['account_id'],
|
||||
'price_unit': inv_amount,
|
||||
'quantity': wizard.qtty or 1.0,
|
||||
'discount': False,
|
||||
'uos_id': res.get('uos_id', False),
|
||||
'product_id': wizard.product_id.id,
|
||||
'invoice_line_tax_ids': res.get('invoice_line_tax_ids'),
|
||||
'account_analytic_id': sale.project_id.id or False,
|
||||
}
|
||||
inv_values = {
|
||||
'name': sale.client_order_ref or sale.name,
|
||||
'origin': sale.name,
|
||||
'type': 'out_invoice',
|
||||
'reference': False,
|
||||
'account_id': sale.partner_id.property_account_receivable_id.id,
|
||||
'partner_id': sale.partner_invoice_id.id,
|
||||
'invoice_line_ids': [(0, 0, inv_line_values)],
|
||||
'currency_id': sale.pricelist_id.currency_id.id,
|
||||
'comment': sale.note,
|
||||
'payment_term_id': sale.payment_term_id.id,
|
||||
'fiscal_position_id': sale.fiscal_position_id.id or sale.partner_id.property_account_position_id.id,
|
||||
'team_id': sale.team_id.id,
|
||||
}
|
||||
result.append((sale.id, inv_values))
|
||||
return result
|
||||
|
||||
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.compute_taxes(cr, uid, [inv_id], context=context)
|
||||
# add the invoice to the sales order's invoices
|
||||
sale_obj.write(cr, uid, sale_id, {'invoice_ids': [(4, inv_id)]}, context=context)
|
||||
return inv_id
|
||||
|
||||
def create_invoices(self, cr, uid, ids, context=None):
|
||||
""" create invoices for the active sales 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 sales 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 sales order lines to invoice
|
||||
res = act_window.for_xml_id(cr, uid, 'sale', 'action_order_line_tree2', context)
|
||||
res['context'] = {
|
||||
'search_default_uninvoiced': 1
|
||||
}
|
||||
res['domain'] = [('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)
|
||||
if self.advance_payment_method == 'delivered':
|
||||
sale_orders.action_invoice_create()
|
||||
elif self.advance_payment_method == 'all':
|
||||
sale_orders.action_invoice_create(final=True)
|
||||
else:
|
||||
sale_line_obj = self.env['sale.order.line']
|
||||
for order in sale_orders:
|
||||
if self.advance_payment_method == 'percentage':
|
||||
amount = order.amount_untaxed * self.amount / 100
|
||||
else:
|
||||
amount = self.amount
|
||||
if self.product_id.invoice_policy != 'order':
|
||||
raise UserError(_('The product used to invoice a deposit should have an invoice policy set to "Ordered quantities". Please update your deposit product to be able to create a deposit invoice.'))
|
||||
if self.product_id.type != 'service':
|
||||
raise UserError(_("The product used to invoice an deposit should be of type 'Service'. Please use another product or update this product."))
|
||||
so_line = sale_line_obj.create({
|
||||
'name': _('Advance: %s') % (time.strftime('%m %Y'),),
|
||||
'price_unit': amount,
|
||||
'product_uom_qty': 0.0,
|
||||
'order_id': order.id,
|
||||
'discount': 0.0,
|
||||
'product_uom': self.product_id.uom_id.id,
|
||||
'product_id': self.product_id.id,
|
||||
'tax_id': self.product_id.taxes_id,
|
||||
})
|
||||
self._create_invoice(order, so_line, amount)
|
||||
if self._context.get('open_invoices', False):
|
||||
return sale_orders.action_view_invoice()
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
def open_invoices(self, cr, uid, ids, invoice_ids, context=None):
|
||||
""" open a view on one of the given invoice_ids """
|
||||
ir_model_data = self.pool.get('ir.model.data')
|
||||
form_res = ir_model_data.get_object_reference(cr, uid, 'account', 'invoice_form')
|
||||
form_id = form_res and form_res[1] or False
|
||||
tree_res = ir_model_data.get_object_reference(cr, uid, 'account', 'invoice_tree')
|
||||
tree_id = tree_res and tree_res[1] or False
|
||||
|
||||
return {
|
||||
'name': _('Advance Invoice'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form,tree',
|
||||
'res_model': 'account.invoice',
|
||||
'res_id': invoice_ids[0],
|
||||
'view_id': False,
|
||||
'views': [(form_id, 'form'), (tree_id, 'tree')],
|
||||
'context': "{'type': 'out_invoice'}",
|
||||
'type': 'ir.actions.act_window',
|
||||
}
|
||||
|
||||
@@ -2,23 +2,21 @@
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="view_sale_advance_payment_inv" model="ir.ui.view">
|
||||
<field name="name">Invoice Order</field>
|
||||
<field name="name">Invoice Orders</field>
|
||||
<field name="model">sale.advance.payment.inv</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Invoice Sales Order">
|
||||
<p class="oe_grey">
|
||||
Select how you want to invoice this order. This
|
||||
will create a draft invoice that can be modified
|
||||
before validation.
|
||||
Invoices will be created in draft so that you can update
|
||||
them before validation.
|
||||
</p>
|
||||
<group>
|
||||
<field name="count" invisible="[('count','=',1)]" readonly="True"/>
|
||||
<field name="advance_payment_method" class="oe_inline" widget="radio"
|
||||
on_change="onchange_method(advance_payment_method, product_id)"/>
|
||||
<field name="qtty" invisible="1"/>
|
||||
attrs="{'invisible': [('count','>',1)]}"/>
|
||||
<field name="product_id"
|
||||
on_change="onchange_method(advance_payment_method, product_id)"
|
||||
context="{'search_default_services': 1}"
|
||||
attrs="{'invisible': [('advance_payment_method','!=','fixed')]}"/>
|
||||
context="{'search_default_services': 1, 'default_type': 'service', 'default_invoice_policy': 'order'}" class="oe_inline"
|
||||
attrs="{'invisible': [('advance_payment_method','not in', ('fixed','percentage'))]}"/>
|
||||
<label for="amount" attrs="{'invisible': [('advance_payment_method', 'not in', ('fixed','percentage'))]}"/>
|
||||
<div attrs="{'invisible': [('advance_payment_method', 'not in', ('fixed','percentage'))]}">
|
||||
<field name="amount"
|
||||
@@ -27,19 +25,12 @@
|
||||
attrs="{'invisible': [('advance_payment_method', '!=', 'percentage')]}" class="oe_inline"/>
|
||||
</div>
|
||||
</group>
|
||||
<div>
|
||||
<b><label string="After clicking 'Show Lines to Invoice', select lines to invoice and create the invoice from the 'More' dropdown menu." attrs="{'invisible': [('advance_payment_method', '!=', 'lines')]}"/></b>
|
||||
</div>
|
||||
<footer>
|
||||
<button name="create_invoices" string="Create and View Invoice" type="object"
|
||||
context="{'open_invoices': True}" class="btn-primary"
|
||||
attrs="{'invisible': [('advance_payment_method', '=', 'lines')]}"/>
|
||||
<button name="create_invoices" string="Create Invoice" type="object"
|
||||
class="btn-primary"
|
||||
attrs="{'invisible': [('advance_payment_method', '=', 'lines')]}"/>
|
||||
<button name="create_invoices" string="Show Lines to Invoice" type="object"
|
||||
class="btn-primary"
|
||||
attrs="{'invisible': [('advance_payment_method', '!=', 'lines')]}"/>
|
||||
<button name="create_invoices" string="Create and View Invoices" type="object"
|
||||
context="{'open_invoices': True}" class="btn-primary"/>
|
||||
<button name="create_invoices" string="Create Invoices" type="object"
|
||||
class="btn-primary"/>
|
||||
or
|
||||
<button string="Cancel" class="btn-default" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
@@ -54,5 +45,16 @@
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<!-- TODO: check if we need this -->
|
||||
<record model="ir.values" id="sale_order_line_make_invoice">
|
||||
<field name="model_id" ref="sale.model_sale_order_line" />
|
||||
<field name="name">Invoice Orders</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_view_sale_advance_payment_inv'))" />
|
||||
<field name="key">action</field>
|
||||
<field name="model">sale.order</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
Reference in New Issue
Block a user