Master haresh 05072018
This commit is contained in:
parent
a6b79054ac
commit
90389d2065
@ -203,8 +203,12 @@ class PurchaseOrder(models.Model):
|
||||
self.payment_term_id = payment_term.id,
|
||||
self.company_id = requisition.company_id.id
|
||||
self.currency_id = currency.id
|
||||
if not self.origin or requisition.name not in self.origin.split(', '):
|
||||
if self.origin:
|
||||
if requisition.name:
|
||||
self.origin = self.origin + ', ' + requisition.name
|
||||
else:
|
||||
self.origin = requisition.name
|
||||
self.partner_ref = requisition.name # to control vendor bill based on agreement reference
|
||||
self.notes = requisition.description
|
||||
self.date_order = requisition.date_end or fields.Datetime.now()
|
||||
self.picking_type_id = requisition.picking_type_id.id
|
||||
|
@ -18,6 +18,8 @@ from flectra.tools.float_utils import float_compare
|
||||
|
||||
|
||||
def float_to_time(float_hour):
|
||||
if float_hour == 24.0:
|
||||
return datetime.time.max
|
||||
return datetime.time(int(math.modf(float_hour)[1]), int(60 * math.modf(float_hour)[0]), 0)
|
||||
|
||||
|
||||
@ -643,7 +645,9 @@ class ResourceCalendarAttendance(models.Model):
|
||||
], 'Day of Week', required=True, index=True, default='0')
|
||||
date_from = fields.Date(string='Starting Date')
|
||||
date_to = fields.Date(string='End Date')
|
||||
hour_from = fields.Float(string='Work from', required=True, index=True, help="Start and End time of working.")
|
||||
hour_from = fields.Float(string='Work from', required=True, index=True,
|
||||
help="Start and End time of working.\n"
|
||||
"A specific value of 24:00 is interpreted as 23:59:59.999999.")
|
||||
hour_to = fields.Float(string='Work to', required=True)
|
||||
calendar_id = fields.Many2one("resource.calendar", string="Resource's Calendar", required=True, ondelete='cascade')
|
||||
|
||||
|
@ -243,6 +243,21 @@ class ResourceWorkingHours(TestResourceCommon):
|
||||
compute_leaves=True)
|
||||
self.assertEqual(res, 33.0)
|
||||
|
||||
def test_calendar_working_hours_24(self):
|
||||
self.att_4 = self.env['resource.calendar.attendance'].create({
|
||||
'name': 'Att4',
|
||||
'calendar_id': self.calendar.id,
|
||||
'dayofweek': '2',
|
||||
'hour_from': 0,
|
||||
'hour_to': 24
|
||||
})
|
||||
res = self.calendar.get_work_hours_count(
|
||||
Datetime.from_string('2018-06-19 23:00:00'),
|
||||
Datetime.from_string('2018-06-21 01:00:00'),
|
||||
self.resource1_id,
|
||||
compute_leaves=True)
|
||||
self.assertAlmostEqual(res, 24.0)
|
||||
|
||||
def test_calendar_timezone(self):
|
||||
# user in timezone UTC-9 asks for work hours
|
||||
# Limits: between 2013-02-19 10:00:00 and 2013-02-26 15:30:00 (User TZ)
|
||||
|
@ -12,6 +12,8 @@ This module contains all the common features of Sales Management and eCommerce.
|
||||
""",
|
||||
'depends': ['sales_team', 'account', 'portal'],
|
||||
'data': [
|
||||
'security/sale_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/ir_sequence_data.xml',
|
||||
'data/sale_data.xml',
|
||||
'report/sale_report.xml',
|
||||
@ -20,8 +22,6 @@ This module contains all the common features of Sales Management and eCommerce.
|
||||
'report/sale_report_templates.xml',
|
||||
'report/invoice_report_templates.xml',
|
||||
'report/report_all_channels_sales_views.xml',
|
||||
'security/sale_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/sale_make_invoice_advance_views.xml',
|
||||
'views/sale_views.xml',
|
||||
'views/account_invoice_views.xml',
|
||||
|
@ -5,7 +5,7 @@
|
||||
<!--Email template -->
|
||||
<record id="email_template_edi_sale" model="mail.template">
|
||||
<field name="name">Sales Order - Send by Email</field>
|
||||
<field name="email_from">${(object.user_id.email and '%s <%s>' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
|
||||
<field name="email_from">${(object.user_id.email and '"%s" <%s>' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
|
||||
<field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field>
|
||||
<field name="partner_to">${object.partner_id.id}</field>
|
||||
<field name="model_id" ref="sale.model_sale_order"/>
|
||||
@ -70,7 +70,7 @@ from ${object.company_id.name}.
|
||||
<field name="body_html"><![CDATA[<html>
|
||||
<head></head>
|
||||
% set record = ctx.get('record')
|
||||
% set company = record and record.company_id or user.company_id
|
||||
% set company = record and record.company_id or ctx.get('company')
|
||||
<body style="margin: 0; padding: 0;">
|
||||
<table border="0" width="100%" cellpadding="0" bgcolor="#ededed" style="padding: 20px; background-color: #ededed; border-collapse:separate;" summary="o_mail_notification">
|
||||
<tbody>
|
||||
|
@ -39,6 +39,9 @@ class AccountInvoice(models.Model):
|
||||
def _onchange_delivery_address(self):
|
||||
addr = self.partner_id.address_get(['delivery'])
|
||||
self.partner_shipping_id = addr and addr.get('delivery')
|
||||
if self.env.context.get('type', 'out_invoice') == 'out_invoice':
|
||||
company = self.company_id or self.env.user.company_id
|
||||
self.comment = company.with_context(lang=self.partner_id.lang).sale_note
|
||||
|
||||
@api.multi
|
||||
def action_invoice_paid(self):
|
||||
@ -96,15 +99,6 @@ class AccountInvoiceLine(models.Model):
|
||||
_inherit = 'account.invoice.line'
|
||||
_order = 'invoice_id, layout_category_id, sequence, id'
|
||||
|
||||
@api.depends('price_unit', 'discount', 'invoice_line_tax_ids', 'quantity',
|
||||
'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id', 'invoice_id.company_id',
|
||||
'invoice_id.date_invoice')
|
||||
def _compute_total_price(self):
|
||||
for line in self:
|
||||
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||
taxes = line.invoice_line_tax_ids.compute_all(price, line.invoice_id.currency_id, line.quantity, product=line.product_id, partner=line.invoice_id.partner_id)
|
||||
line.price_total = taxes['total_included']
|
||||
|
||||
sale_line_ids = fields.Many2many(
|
||||
'sale.order.line',
|
||||
'sale_order_line_invoice_rel',
|
||||
@ -113,4 +107,3 @@ class AccountInvoiceLine(models.Model):
|
||||
layout_category_id = fields.Many2one('sale.layout_category', string='Section')
|
||||
layout_category_sequence = fields.Integer(string='Layout Sequence')
|
||||
# TODO: remove layout_category_sequence in master or make it work properly
|
||||
price_total = fields.Monetary(compute='_compute_total_price', string='Total Amount', store=True)
|
||||
|
@ -23,3 +23,6 @@ class ProductProduct(models.Model):
|
||||
return r
|
||||
|
||||
sales_count = fields.Integer(compute='_sales_count', string='# Sales')
|
||||
|
||||
def _get_invoice_policy(self):
|
||||
return self.invoice_policy
|
||||
|
@ -24,7 +24,7 @@ class ProductTemplate(models.Model):
|
||||
@api.depends('product_variant_ids.sales_count')
|
||||
def _sales_count(self):
|
||||
for product in self:
|
||||
product.sales_count = sum([p.sales_count for p in product.product_variant_ids])
|
||||
product.sales_count = sum([p.sales_count for p in product.with_context(active_test=False).product_variant_ids])
|
||||
|
||||
@api.multi
|
||||
def action_view_sales(self):
|
||||
|
@ -59,7 +59,7 @@ class SaleOrder(models.Model):
|
||||
# Search for invoices which have been 'cancelled' (filter_refund = 'modify' in
|
||||
# 'account.invoice.refund')
|
||||
# use like as origin may contains multiple references (e.g. 'SO01, SO02')
|
||||
refunds = invoice_ids.search([('origin', 'like', order.name)]).filtered(lambda r: r.type in ['out_invoice', 'out_refund'])
|
||||
refunds = invoice_ids.search([('origin', 'like', order.name), ('company_id', '=', order.company_id.id), ('branch_id', '=', order.branch_id.id)]).filtered(lambda r: r.type in ['out_invoice', 'out_refund'])
|
||||
invoice_ids |= refunds.filtered(lambda r: order.name in [origin.strip() for origin in r.origin.split(',')])
|
||||
# Search for refunds as well
|
||||
refund_ids = self.env['account.invoice'].browse()
|
||||
@ -413,6 +413,9 @@ class SaleOrder(models.Model):
|
||||
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
|
||||
invoices = {}
|
||||
references = {}
|
||||
invoices_origin = {}
|
||||
invoices_name = {}
|
||||
|
||||
for order in self:
|
||||
group_key = order.id if grouped else (order.partner_invoice_id.id, order.currency_id.id)
|
||||
for line in order.order_line.sorted(key=lambda l: l.qty_to_invoice < 0):
|
||||
@ -423,13 +426,14 @@ class SaleOrder(models.Model):
|
||||
invoice = inv_obj.create(inv_data)
|
||||
references[invoice] = order
|
||||
invoices[group_key] = invoice
|
||||
invoices_origin[group_key] = [invoice.origin]
|
||||
invoices_name[group_key] = [invoice.name]
|
||||
elif group_key in invoices:
|
||||
vals = {}
|
||||
if order.name not in invoices[group_key].origin.split(', '):
|
||||
vals['origin'] = invoices[group_key].origin + ', ' + order.name
|
||||
if order.client_order_ref and order.client_order_ref not in invoices[group_key].name.split(', ') and order.client_order_ref != invoices[group_key].name:
|
||||
vals['name'] = invoices[group_key].name + ', ' + order.client_order_ref
|
||||
invoices[group_key].write(vals)
|
||||
if order.name not in invoices_origin[group_key]:
|
||||
invoices_origin[group_key].append(order.name)
|
||||
if order.client_order_ref and order.client_order_ref not in invoices_name[group_key]:
|
||||
invoices_name[group_key].append(order.client_order_ref)
|
||||
|
||||
if line.qty_to_invoice > 0:
|
||||
line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice)
|
||||
elif line.qty_to_invoice < 0 and final:
|
||||
@ -437,7 +441,11 @@ class SaleOrder(models.Model):
|
||||
|
||||
if references.get(invoices.get(group_key)):
|
||||
if order not in references[invoices[group_key]]:
|
||||
references[invoice] = references[invoice] | order
|
||||
references[invoices[group_key]] |= order
|
||||
|
||||
for group_key in invoices:
|
||||
invoices[group_key].write({'name': ', '.join(invoices_name[group_key]),
|
||||
'origin': ', '.join(invoices_origin[group_key])})
|
||||
|
||||
if not invoices:
|
||||
raise UserError(_('There is no invoiceable line.'))
|
||||
@ -1084,23 +1092,21 @@ class SaleOrderLine(models.Model):
|
||||
|
||||
@api.multi
|
||||
def name_get(self):
|
||||
if self._context.get('sale_show_order_product_name'):
|
||||
result = []
|
||||
for so_line in self:
|
||||
name = '%s - %s' % (so_line.order_id.name, so_line.product_id.name)
|
||||
name = '%s - %s' % (so_line.order_id.name, so_line.name.split('\n')[0] or so_line.product_id.name)
|
||||
if so_line.order_partner_id.ref:
|
||||
name = '%s (%s)' % (name, so_line.order_partner_id.ref)
|
||||
result.append((so_line.id, name))
|
||||
return result
|
||||
return super(SaleOrderLine, self).name_get()
|
||||
|
||||
@api.model
|
||||
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||
if self._context.get('sale_show_order_product_name'):
|
||||
if operator in ('ilike', 'like', '=', '=like', '=ilike'):
|
||||
domain = expression.AND([
|
||||
args = expression.AND([
|
||||
args or [],
|
||||
['|', ('order_id.name', operator, name), ('name', operator, name)]
|
||||
])
|
||||
return self.search(domain, limit=limit).name_get()
|
||||
return super(SaleOrderLine, self).name_search(name, args, operator, limit)
|
||||
|
||||
@api.multi
|
||||
@ -1202,7 +1208,7 @@ class SaleOrderLine(models.Model):
|
||||
on SO lines. This method is a hook: since analytic line are used for timesheet,
|
||||
expense, ... each use case should provide its part of the domain.
|
||||
"""
|
||||
return [('so_line', 'in', self.ids), ('amount', '<=', 0.0)]
|
||||
return ['&', ('so_line', 'in', self.ids), ('amount', '<=', 0.0)]
|
||||
|
||||
@api.multi
|
||||
def _analytic_compute_delivered_quantity(self):
|
||||
@ -1242,3 +1248,7 @@ class SaleOrderLine(models.Model):
|
||||
so_line.write({'qty_delivered': qty})
|
||||
|
||||
return True
|
||||
|
||||
def _is_delivery(self):
|
||||
self.ensure_one()
|
||||
return False
|
||||
|
@ -22,7 +22,7 @@
|
||||
<th class="text-right">Unit Price</th>
|
||||
<th t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line">Disc.(%)</th>
|
||||
<th class="text-right">Taxes</th>
|
||||
<th class="text-right">Price</th>
|
||||
<th class="text-right">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="invoice_tbody">
|
||||
@ -53,7 +53,7 @@
|
||||
<td class="text-right">
|
||||
<span t-esc="', '.join(map(lambda x: x.description or x.name, l.invoice_line_tax_ids))"/>
|
||||
</td>
|
||||
<td class="text-right" groups="sale.group_show_price_subtotal">
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_subtotal"
|
||||
t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||
</td>
|
||||
|
@ -57,6 +57,7 @@ class PosSaleReport(models.Model):
|
||||
(cr.date_end IS NULL OR cr.date_end > COALESCE(so.date_order, now())))
|
||||
LEFT JOIN product_uom u on (u.id=sol.product_uom)
|
||||
LEFT JOIN product_uom u2 on (u2.id=pt.uom_id)
|
||||
WHERE so.state != 'cancel'
|
||||
""" % self.env['res.currency']._select_companies_rates()
|
||||
return so_str
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
<field name="name">report.all.channels.sales.pivot</field>
|
||||
<field name="model">report.all.channels.sales</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="All Channels Sales Orders Analysis">
|
||||
<pivot string="All Channels Sales Orders Analysis" disable_linking="True">
|
||||
<field name="name" type="row"/>
|
||||
<field name="price_total" string="Total Price" type="measure"/>
|
||||
</pivot>
|
||||
|
@ -16,6 +16,7 @@
|
||||
string="PRO-FORMA Invoice"
|
||||
model="sale.order"
|
||||
report_type="qweb-pdf"
|
||||
groups="sale.group_proforma_sales"
|
||||
file="sale.report_saleorder_pro_forma"
|
||||
name="sale.report_saleorder_pro_forma"
|
||||
print_report_name="'PRO-FORMA - %s' % (object.name)"
|
||||
|
@ -60,6 +60,11 @@
|
||||
<strong>Payment Terms:</strong>
|
||||
<p t-field="doc.payment_term_id"/>
|
||||
</div>
|
||||
<div t-if="doc.validity_date and doc.state in ['draft', 'sent']" class="col-xs-3">
|
||||
<strong>Expiration Date:</strong>
|
||||
<p t-field="doc.validity_date"/>
|
||||
</div>
|
||||
|
||||
<div name="branch" t-if="doc.branch_id" class="col-xs-3" groups="base_branch_company.group_multi_branch">
|
||||
<strong>Branch:</strong>
|
||||
<p t-field="doc.branch_id"/>
|
||||
|
@ -158,9 +158,6 @@
|
||||
<field name="portal_confirmation_options" class="o_light_label" widget="radio"
|
||||
attrs="{'required': [('portal_confirmation', '=', True)]}"/>
|
||||
</div>
|
||||
<div attrs="{'invisible': [('portal_confirmation_options', '!=', 'pay')]}">
|
||||
<button name='%(payment.action_payment_acquirer)d' icon="fa-arrow-right" type="action" string="Payment Acquirers" class="btn-link"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -269,7 +266,7 @@
|
||||
</div>
|
||||
<h2>Invoicing</h2>
|
||||
<div class="row mt16 o_settings_container">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box" title="This default value is applied to any new product created. This can be changed in the product detail form.">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box hidden" title="This default value is applied to any new product created. This can be changed in the product detail form.">
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="default_invoice_policy"/>
|
||||
<div class="text-muted">
|
||||
|
@ -12,8 +12,6 @@
|
||||
Create a Quotation, the first step of a new sale.
|
||||
</p><p>
|
||||
Your next actions should flow efficiently: confirm the Quotation to a Sales Order, then create the Invoice and collect the Payment.
|
||||
</p><p>
|
||||
Note that once a Quotation becomes a Sales Order, it will be moved from the Quotations list to the Sales Order list.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -148,7 +148,7 @@
|
||||
<span class="label label-default"><i class="fa fa-fw fa-remove"/> Cancelled</span>
|
||||
</t>
|
||||
<t t-if="order.state == 'done'">
|
||||
<span class="label label-default"><i class="fa fa-fw fa-remove"/> Done</span>
|
||||
<span class="label label-success"><i class="fa fa-fw fa-check" /> Done</span>
|
||||
</t>
|
||||
</h4>
|
||||
</div>
|
||||
|
@ -586,9 +586,6 @@
|
||||
</p><p>
|
||||
Your next actions should flow efficiently: confirm the Quotation
|
||||
to a Sales Order, then create the Invoice and collect the Payment.
|
||||
</p><p>
|
||||
Note that once a Quotation becomes a Sales Order, it will be moved
|
||||
from the Quotations list to the Sales Order list.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
@ -839,8 +836,6 @@
|
||||
Create a Quotation, the first step of a new sale.
|
||||
</p><p>
|
||||
Your next actions should flow efficiently: confirm the Quotation to a Sales Order, then create the Invoice and collect the Payment.
|
||||
</p><p>
|
||||
Note that once a Quotation becomes a Sales Order, it will be moved from the Quotations list to the Sales Order list.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
@ -880,9 +875,6 @@
|
||||
</p><p>
|
||||
Your next actions should flow efficiently: confirm the Quotation
|
||||
to a Sales Order, then create the Invoice and collect the Payment.
|
||||
</p><p>
|
||||
Note that once a Quotation becomes a Sales Order, it will be moved
|
||||
from the Quotations list to the Sales Order list.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
@ -65,7 +65,7 @@ class SaleAdvancePaymentInv(models.TransientModel):
|
||||
|
||||
account_id = False
|
||||
if self.product_id.id:
|
||||
account_id = self.product_id.property_account_income_id.id
|
||||
account_id = self.product_id.property_account_income_id.id or self.product_id.categ_id.property_account_income_categ_id.id
|
||||
if not account_id:
|
||||
inc_acc = ir_property_obj.get('property_account_income_categ_id', 'product.category')
|
||||
account_id = order.fiscal_position_id.map_account(inc_acc).id if inc_acc else False
|
||||
@ -179,4 +179,5 @@ class SaleAdvancePaymentInv(models.TransientModel):
|
||||
'invoice_policy': 'order',
|
||||
'property_account_income_id': self.deposit_account_id.id,
|
||||
'taxes_id': [(6, 0, self.deposit_taxes_id.ids)],
|
||||
'company_id': False,
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ tour.register('sale_tour', {
|
||||
in_modal: false,
|
||||
run: function (actions) {
|
||||
actions.auto();
|
||||
// There might be a modal because of the view:
|
||||
// sale.order.form.editable.list, enabled by some groups
|
||||
if ($(".modal-footer .btn-primary").length) {
|
||||
actions.auto(".modal-footer .btn-primary");
|
||||
}
|
||||
|
@ -1,20 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from functools import partial
|
||||
import flectra
|
||||
from flectra import api, SUPERUSER_ID
|
||||
|
||||
from . import models # noqa
|
||||
from . import report # noqa
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
def recreate_view(dbname):
|
||||
db_registry = openerp.modules.registry.Registry.new(dbname)
|
||||
with api.Environment.manage(), db_registry.cursor() as cr:
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
if 'sale.report' in env:
|
||||
env['sale.report'].init()
|
||||
|
||||
cr.after("commit", partial(recreate_view, cr.dbname))
|
||||
|
@ -16,5 +16,4 @@ Price and Cost Price.
|
||||
'depends':['sale_management'],
|
||||
'demo':['data/sale_margin_demo.xml'],
|
||||
'data':['security/ir.model.access.csv','views/sale_margin_view.xml'],
|
||||
'uninstall_hook': "uninstall_hook",
|
||||
}
|
||||
|
@ -62,8 +62,7 @@ class AccountInvoiceLine(models.Model):
|
||||
qty_done = sum([x.uom_id._compute_quantity(x.quantity, x.product_id.uom_id) for x in s_line.invoice_lines if x.invoice_id.state in ('open', 'paid')])
|
||||
quantity = self.uom_id._compute_quantity(self.quantity, self.product_id.uom_id)
|
||||
# Put moves in fixed order by date executed
|
||||
moves = s_line.move_ids
|
||||
moves.sorted(lambda x: x.date)
|
||||
moves = s_line.move_ids.sorted(lambda x: x.date)
|
||||
# Go through all the moves and do nothing until you get to qty_done
|
||||
# Beyond qty_done we need to calculate the average of the price_unit
|
||||
# on the moves we encounter.
|
||||
|
@ -33,7 +33,7 @@ class SaleOrder(models.Model):
|
||||
for order in self:
|
||||
dates_list = []
|
||||
order_datetime = fields.Datetime.from_string(order.date_order)
|
||||
for line in order.order_line.filtered(lambda x: x.state != 'cancel'):
|
||||
for line in order.order_line.filtered(lambda x: x.state != 'cancel' and not x._is_delivery()):
|
||||
dt = order_datetime + timedelta(days=line.customer_lead or 0.0)
|
||||
dates_list.append(dt)
|
||||
if dates_list:
|
||||
|
@ -17,7 +17,6 @@ class PaymentPortal(http.Controller):
|
||||
:return html: form containing all values related to the acquirer to
|
||||
redirect customers to the acquirer website """
|
||||
success_url = kwargs.get('success_url', '/my')
|
||||
callback_method = kwargs.get('callback_method', '')
|
||||
|
||||
order_sudo = request.env['sale.order'].sudo().browse(order_id)
|
||||
if not order_sudo:
|
||||
@ -28,17 +27,16 @@ class PaymentPortal(http.Controller):
|
||||
except:
|
||||
return False
|
||||
|
||||
if request.env.user == request.env.ref('base.public_user'):
|
||||
save_token = False
|
||||
|
||||
token = request.env['payment.token'].sudo() # currently no support of payment tokens
|
||||
tx = request.env['payment.transaction'].sudo()._check_or_create_sale_tx(
|
||||
order_sudo,
|
||||
acquirer,
|
||||
payment_token=token,
|
||||
tx_type='form_save' if save_token else 'form',
|
||||
add_tx_values={
|
||||
'callback_model_id': request.env['ir.model'].sudo().search([('model', '=', order_sudo._name)], limit=1).id,
|
||||
'callback_res_id': order_sudo.id,
|
||||
'callback_method': callback_method,
|
||||
})
|
||||
)
|
||||
|
||||
# set the transaction id into the session
|
||||
request.session['portal_sale_%s_transaction_id' % order_sudo.id] = tx.id
|
||||
@ -58,7 +56,6 @@ class PaymentPortal(http.Controller):
|
||||
""" Use a token to perform a s2s transaction """
|
||||
error_url = kwargs.get('error_url', '/my')
|
||||
success_url = kwargs.get('success_url', '/my')
|
||||
callback_method = kwargs.get('callback_method', '')
|
||||
access_token = kwargs.get('access_token')
|
||||
params = {}
|
||||
if access_token:
|
||||
@ -73,7 +70,9 @@ class PaymentPortal(http.Controller):
|
||||
token = request.env['payment.token'].sudo().browse(int(pm_id))
|
||||
except (ValueError, TypeError):
|
||||
token = False
|
||||
if not token:
|
||||
|
||||
token_owner = order_sudo.partner_id if request.env.user == request.env.ref('base.public_user') else request.env.user.partner_id
|
||||
if not token or token.partner_id != token_owner:
|
||||
params['error'] = 'pay_sale_invalid_token'
|
||||
return request.redirect(_build_url_w_params(error_url, params))
|
||||
|
||||
@ -83,11 +82,7 @@ class PaymentPortal(http.Controller):
|
||||
token.acquirer_id,
|
||||
payment_token=token,
|
||||
tx_type='server2server',
|
||||
add_tx_values={
|
||||
'callback_model_id': request.env['ir.model'].sudo().search([('model', '=', order_sudo._name)], limit=1).id,
|
||||
'callback_res_id': order_sudo.id,
|
||||
'callback_method': callback_method,
|
||||
})
|
||||
)
|
||||
|
||||
# set the transaction id into the session
|
||||
request.session['portal_sale_%s_transaction_id' % order_sudo.id] = tx.id
|
||||
|
@ -10,5 +10,16 @@ class CustomerPortal(CustomerPortal):
|
||||
def _order_get_page_view_values(self, order, access_token, **kwargs):
|
||||
values = super(CustomerPortal, self)._order_get_page_view_values(order, access_token, **kwargs)
|
||||
if values['portal_confirmation'] == 'pay':
|
||||
values.update(request.env['payment.acquirer']._get_available_payment_input(order.partner_id, order.company_id))
|
||||
payment_inputs = request.env['payment.acquirer']._get_available_payment_input(order.partner_id, order.company_id)
|
||||
# if not connected (using public user), the method _get_available_payment_input will return public user tokens
|
||||
is_public_user = request.env.ref('base.public_user') == request.env.user
|
||||
if is_public_user:
|
||||
# we should not display payment tokens owned by the public user
|
||||
payment_inputs.pop('pms', None)
|
||||
token_count = request.env['payment.token'].sudo().search_count([('acquirer_id.company_id', '=', order.company_id.id),
|
||||
('partner_id', '=', order.partner_id.id),
|
||||
])
|
||||
values['existing_token'] = token_count > 0
|
||||
values.update(payment_inputs)
|
||||
values['partner_id'] = order.partner_id if is_public_user else request.env.user.partner_id
|
||||
return values
|
||||
|
@ -3,6 +3,7 @@
|
||||
import logging
|
||||
|
||||
from flectra import api, fields, models, _
|
||||
from flectra.exceptions import UserError
|
||||
from flectra.tools import float_compare
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@ -68,7 +69,6 @@ class PaymentTransaction(models.Model):
|
||||
_logger.info('<%s> transaction completed, auto-confirming order %s (ID %s)', self.acquirer_id.provider, self.sale_order_id.name, self.sale_order_id.id)
|
||||
if self.sale_order_id.state in ('draft', 'sent'):
|
||||
self.sale_order_id.with_context(send_email=True).action_confirm()
|
||||
self._generate_and_pay_invoice()
|
||||
elif self.state not in ['cancel', 'error'] and self.sale_order_id.state == 'draft':
|
||||
_logger.info('<%s> transaction pending/to confirm manually, sending quote email for order %s (ID %s)', self.acquirer_id.provider, self.sale_order_id.name, self.sale_order_id.id)
|
||||
self.sale_order_id.force_quotation_send()
|
||||
@ -85,8 +85,16 @@ class PaymentTransaction(models.Model):
|
||||
# force_company needed for company_dependent fields
|
||||
ctx_company = {'company_id': self.sale_order_id.company_id.id,
|
||||
'force_company': self.sale_order_id.company_id.id}
|
||||
|
||||
# We might fail to create the invoice because there is no invoiceable lines. This will
|
||||
# raise a UserError and break the workflow. Better catch the error.
|
||||
try:
|
||||
created_invoice = self.sale_order_id.with_context(**ctx_company).action_invoice_create()
|
||||
created_invoice = self.env['account.invoice'].browse(created_invoice).with_context(**ctx_company)
|
||||
except UserError:
|
||||
_logger.warning('<%s> transaction completed, could not auto-generate invoice for %s (ID %s)',
|
||||
self.acquirer_id.provider, self.sale_order_id.name, self.sale_order_id.id, exc_info=True)
|
||||
return
|
||||
|
||||
if created_invoice:
|
||||
_logger.info('<%s> transaction completed, auto-generated invoice %s (ID %s) for %s (ID %s)',
|
||||
@ -174,7 +182,7 @@ class PaymentTransaction(models.Model):
|
||||
'currency_id': order.pricelist_id.currency_id.id,
|
||||
'partner_id': order.partner_id.id,
|
||||
'partner_country_id': order.partner_id.country_id.id,
|
||||
'reference': self.get_next_reference(order.name),
|
||||
'reference': self._get_next_reference(order.name, acquirer=acquirer),
|
||||
'sale_order_id': order.id,
|
||||
}
|
||||
if add_tx_values:
|
||||
|
@ -67,6 +67,11 @@
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body" t-if="existing_token">
|
||||
<div class="col-md-offset-3 col-md-6">
|
||||
<i class="fa fa-info"></i> You have credits card registered, you can log-in to be able to use them.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//t[@t-if='invoices']" position="before">
|
||||
|
@ -1,5 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra import api, SUPERUSER_ID
|
||||
|
||||
from . import models
|
||||
from . import controllers
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
env['product.template'].search([
|
||||
('service_type', '=', 'timesheet')
|
||||
]).write({'service_type': 'manual'})
|
||||
env['product.product'].search([
|
||||
('service_type', '=', 'timesheet')
|
||||
]).write({'service_type': 'manual'})
|
||||
|
@ -31,4 +31,5 @@ have real delivered quantities in sales orders.
|
||||
'data/sale_service_demo.xml',
|
||||
],
|
||||
'auto_install': True,
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
}
|
||||
|
@ -56,8 +56,8 @@ class SaleTimesheetController(http.Controller):
|
||||
dashboard_values['hours'][billable_type] = float_round(data.get('unit_amount'), precision_rounding=hour_rounding)
|
||||
dashboard_values['hours']['total'] += float_round(data.get('unit_amount'), precision_rounding=hour_rounding)
|
||||
# rates
|
||||
dashboard_values['rates'][billable_type] = round(data.get('unit_amount') / dashboard_total_hours * 100, 2)
|
||||
dashboard_values['rates']['total'] += round(data.get('unit_amount') / dashboard_total_hours * 100, 2)
|
||||
dashboard_values['rates'][billable_type] = dashboard_total_hours and round(data.get('unit_amount') / dashboard_total_hours * 100, 2) or 0
|
||||
dashboard_values['rates']['total'] += dashboard_total_hours and round(data.get('unit_amount') / dashboard_total_hours * 100, 2) or 0
|
||||
|
||||
# money_amount
|
||||
so_lines = values['timesheet_lines'].mapped('so_line')
|
||||
|
@ -99,7 +99,8 @@ class AccountInvoice(models.Model):
|
||||
line_revenue = timesheet_line.timesheet_revenue * price_subtotal_inv / price_subtotal_sol
|
||||
total_revenue_per_currency[timesheet_line.company_currency_id.id] += line_revenue
|
||||
else:
|
||||
total_revenue_per_currency[timesheet_line.company_currency_id.id] += timesheet_line.timesheet_revenue
|
||||
line_revenue = timesheet_line.timesheet_revenue
|
||||
total_revenue_per_currency[timesheet_line.company_currency_id.id] += line_revenue
|
||||
else: # last line: add the difference to avoid rounding problem
|
||||
last_price_subtotal_inv = invoice_line.currency_id.compute(invoice_line.price_subtotal, timesheet_line.company_currency_id)
|
||||
total_revenue = sum([self.env['res.currency'].browse(currency_id).compute(amount, timesheet_line.company_currency_id) for currency_id, amount in total_revenue_per_currency.items()])
|
||||
|
@ -70,8 +70,9 @@ class ProjectTask(models.Model):
|
||||
@api.model
|
||||
def create(self, values):
|
||||
# sub task has the same so line than their parent
|
||||
if 'parent_id' in values:
|
||||
values['sale_line_id'] = self.env['project.task'].browse(values['parent_id']).sudo().sale_line_id.id
|
||||
parent_id = values['parent_id'] if 'parent_id' in values else self.env.context.get('default_parent_id')
|
||||
if parent_id:
|
||||
values['sale_line_id'] = self.env['project.task'].browse(parent_id).sudo().sale_line_id.id
|
||||
return super(ProjectTask, self).create(values)
|
||||
|
||||
@api.multi
|
||||
|
@ -175,6 +175,7 @@ class SaleOrderLine(models.Model):
|
||||
@api.multi
|
||||
def _analytic_compute_delivered_quantity_domain(self):
|
||||
domain = super(SaleOrderLine, self)._analytic_compute_delivered_quantity_domain()
|
||||
domain = expression.AND([domain, [('project_id', '=', False)]])
|
||||
timesheet_domain = self._timesheet_compute_delivered_quantity_domain()
|
||||
return expression.OR([domain, timesheet_domain])
|
||||
|
||||
|
@ -17,6 +17,11 @@ class TestSaleTimesheet(TestSale):
|
||||
# after the 6 june of current year.
|
||||
self.env.ref('base.rateUSDbis').unlink()
|
||||
|
||||
# Make sure the company is in USD
|
||||
self.env.cr.execute(
|
||||
"UPDATE res_company SET currency_id = %s WHERE id = %s",
|
||||
[self.env.ref('base.USD').id, self.env.user.company_id.id])
|
||||
|
||||
# create project
|
||||
self.project = self.env['project.project'].create({
|
||||
'name': 'Project for my timesheets',
|
||||
|
@ -29,7 +29,7 @@
|
||||
string="Sales Order"/>
|
||||
</xpath>
|
||||
<field name="user_id" position="after">
|
||||
<field name="sale_line_id" string="Sales Order Item" attrs="{'invisible': [('partner_id', '=', False)], 'readonly': [('parent_id', '!=', False)]}" options='{"no_open": True, "no_create": True}' context="{'sale_show_order_product_name': True}"/>
|
||||
<field name="sale_line_id" string="Sales Order Item" attrs="{'invisible': [('partner_id', '=', False)], 'readonly': [('parent_id', '!=', False)]}" options='{"no_open": True, "no_create": True}'/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
Loading…
Reference in New Issue
Block a user