Compare commits

...

68 Commits

Author SHA1 Message Date
Pierrick brun
29481d91b7 account.move.line: Add filter on both debit and credit
picked from @alexis-via (d4fcaa7d14)
2019-01-14 16:28:04 +01:00
Sébastien BEAU
fc45c44280 [IMP] add some extra style css support and add a debugger mode. Update readme 2019-01-08 23:27:23 +01:00
Sébastien BEAU
a370c9100c [IMP] remove the fucking auto_delete!!! 2019-01-08 23:26:12 +01:00
Sébastien BEAU
e5d6ef4bad [REF] refactor the code to make it simplifier and avoid hacking the _notify method 2019-01-08 23:24:59 +01:00
David Beal
c3f1d30bdd Merge pull request #81 from akretion/10.0-sale_partner_shipping_filter
10.0 sale partner shipping filter
2019-01-04 12:48:23 +01:00
Pierrick brun
6405be3fcf [MIG] 10.0 2019-01-04 11:11:39 +01:00
chafique-delli
7a65c63f99 replace partner_parent_id by commercial_partner_id 2019-01-04 11:00:33 +01:00
chafique-delli
13d8100d98 fix after @bealdav's comment 2019-01-04 11:00:33 +01:00
chafique-delli
0151c6a6e3 domain improvement 2019-01-04 11:00:33 +01:00
chafique-delli
ea5fdcf4df fix domain 2019-01-04 11:00:33 +01:00
chafique-delli
4ff06be1a6 add sale_partner_shipping_filter_with_customer module 2019-01-04 11:00:33 +01:00
David Beal
6c3c6cd43b Merge pull request #72 from akretion/10-fix-py3o-lines-sorting
[FIX] keep the order line sorted when creating the layout_lines dict
2019-01-02 18:56:18 +01:00
Benoît Guillot
3fef559aac [IMP] add domain on partner_id field in account_move and account_move_line search views (#73) 2019-01-02 18:55:15 +01:00
Alexis de Lattre
75d7b7eac4 Add script fix_invoice_attachment_filename 2018-12-20 11:20:31 +01:00
Sébastien BEAU
4a2fff177c [IMP] add balance in view 2018-12-18 23:25:23 +01:00
Sébastien BEAU
e83238becd [IMP] hide odoo report menu and hide the button_cancel on bacnk_statement as the code do not allow to cancel it 2018-12-18 19:53:39 +01:00
Alexis de Lattre
8bebd1e2ef Add @api.model on method that should use it
Improve log msg
2018-12-17 11:17:43 +01:00
Alexis de Lattre
b23f03f79d Add src and dest location on prodlot selection popup 2018-12-05 21:23:43 +01:00
Mourad EL HADJ MIMOUNE
dc8363d6d1 [FIX] add start_date,end_date in statement.display_name 2018-12-04 16:39:57 +01:00
Alexis de Lattre
ad850024ec Script for account.group now works in multi-company envir 2018-12-04 16:33:23 +01:00
Sébastien BEAU
03564a20b2 [IMP] add readme, remove auto following when sending an email, use light version of email notification to avoid injecting useless link in the mail sent 2018-12-04 11:55:57 +01:00
Alexis de Lattre
c1618166fb Improve account group generation 2018-11-29 21:57:19 +01:00
Alexis de Lattre
cf2464dfa4 Add module sale_order_full_dropship 2018-11-29 12:34:34 +01:00
Alexis de Lattre
80191002b8 Fix typo and code cleanup 2018-11-28 17:15:43 +01:00
Alexis de Lattre
694c800df3 Fix visibility of invoice_print button on invoice form 2018-11-28 16:04:18 +01:00
Alexis de Lattre
bdf51029c7 Add script to create account groups 2018-11-22 21:25:40 +01:00
Alexis de Lattre
478ab1fc2b account_usability: improve display of reconcile information, in particular partial reconcile
Warning: on existing big databases, this upgrade will take a long time
because there is a new computed stored field on account.move.line. But it is
required to keep good perfs on tree view of move lines.
2018-11-22 16:26:27 +01:00
Alexis de Lattre
362dba5f90 Restore drill-through on sale and invoice reports 2018-11-21 18:15:25 +01:00
Alexis de Lattre
ac2b70b66e Add margin in sale.report 2018-11-21 16:44:05 +01:00
Alexis de Lattre
b75a2e47a2 Add margin in invoice report
Consequence: no more need for module account_invoice_margin_report
2018-11-21 16:22:10 +01:00
Pierrick Brun
3f73f15e4a [ADD] confirm on reset_real_qty for stock.inventory 2018-11-20 15:27:01 +01:00
Alexis de Lattre
c196343ec0 Improve usability of account.move creation/edition
Default value for account_id, debit, credit, similar to v8 behavior
2018-11-19 19:21:23 +01:00
Alexis de Lattre
7d359d6730 Fix typo 2018-11-19 11:09:37 +01:00
Alexis de Lattre
904bf6c4f4 Cut name_get() of invoice if too long (which screws-up the invoice form view because of the ariane thread at the top) 2018-11-19 10:52:04 +01:00
Alexis de Lattre
e13a2aba3d Remove menu entry for analytic tags
Hide field tag_ids on form view of analytic accounts
2018-11-12 18:27:02 +01:00
Alexis de Lattre
8cb880771e Add module stock_user_default_warehouse_mrp
Fix module stock_user_default_warehouse_purchase
2018-11-02 11:54:21 +01:00
Alexis de Lattre
087bb1fde2 MRP production form: move src/dest loc to the top 2018-10-26 15:46:17 +02:00
Pierrick Brun
806b1b4a86 Merge pull request #68 from yvaucher/10.0-account_invoice_update_wizard-analytic
[10.0] account_invoice_update_wizard analytic fields
2018-10-22 13:56:46 +02:00
Yannick Vaucher
23519f4027 Merge pull request #1 from akretion/10.0-account_invoice_update_wizard-analytic
[FIX] only show analytics to users in group
2018-10-19 14:57:56 +02:00
Pierrick Brun
b54ec10f10 Merge pull request #63 from akretion/10.0-mig-product_search_supplier_code
[MIG] 10.0 product_search_supplier_code
2018-10-17 10:08:06 +02:00
Benoit
118dd2a5c0 [FIX] keep invoice lines sorted when creating the layout_lines dict 2018-09-28 17:42:31 +02:00
Hpar
0afe9a39a6 Merge pull request #57 from akretion/10-product_unit_manager_group-migration
10 product unit manager group migration
2018-09-25 11:56:47 +02:00
Hpar
159f163da5 Merge pull request #61 from akretion/10-add-purchase_buyer
Add purchase buyer module
2018-09-21 14:30:29 +02:00
Hpar
d0620a4c83 Update README.rst 2018-09-21 14:30:18 +02:00
Alexis de Lattre
5d2d5b1e63 Revert my previous commit: use partner_bank_active instead
The module partner_bank_active is avail in OCA/partner-contact
2018-09-18 23:19:09 +02:00
Alexis de Lattre
ab1144850b Fix crash when several quotes are linked to the opportunity 2018-09-18 23:03:21 +02:00
Alexis de Lattre
4995403bf5 Add active field on res.partner.bank 2018-09-18 21:32:16 +02:00
Alexis de Lattre
a415744f11 Add widget=handle on sequence of res.partner.bank 2018-09-17 11:18:01 +02:00
Benoit
c943b4cd33 [FIX] keep the order line sorted when creating the layout_lines dict 2018-09-14 17:46:15 +02:00
Alexis de Lattre
8800b94e5b Fix bad port of name_get() of account.analytic.account to v10 2018-09-13 11:56:37 +02:00
beau sebastien
ca5238a03c Merge pull request #70 from akretion/10.0-better-wizard-for-mail-test
[IMP] improve the wizard for testing email, allow to search on object and to send email for real check
2018-09-04 15:54:59 +02:00
Sébastien BEAU
5f15d83c3c [IMP] improve the wizard for testing email, allow to search on object and to send email for real check 2018-09-04 15:50:02 +02:00
Alexis de Lattre
6370dc0ec8 Port base_user_auth_log to v10 2018-09-02 22:39:49 +02:00
Alexis de Lattre
0d27c0a830 Add module base_user_auth_log 2018-09-02 22:21:05 +02:00
Alexis de Lattre
5f6107f2e8 Add a patch to have analytic in case of writeoff in the register payment
wizard
2018-07-23 16:20:38 +02:00
Alexis de Lattre
ebb8f1ad86 carrier_id not readonly on done picking (add tracking on it)
fix for formatLang inherit in base_usability
2018-07-18 14:27:21 +02:00
Alexis de Lattre
04118bbf46 account_usability: Add copy=False on some fields 2018-07-16 17:16:15 +02:00
Alexis de Lattre
9cba31b68a Show title not only on Contacts 2018-07-13 15:43:46 +02:00
Alexis de Lattre
21d1454ab9 Use untaxed amount in name_get of purchase orders
Add sum for qty in operation lines
2018-07-12 23:53:26 +02:00
Alexis de Lattre
d4e673103e Show 'base' field in tax lines on invoice form view 2018-07-09 17:44:55 +02:00
Alexis de Lattre
9ebf7cdb4c FIX my previous commit: related_sudo -> compute_sudo 2018-07-09 11:43:22 +02:00
Alexis de Lattre
f34a731d95 Add related_sudo where it may be needed
PEP8 fix
2018-07-09 10:41:53 +02:00
Alexis de Lattre
6ef322be4c Improve invoice line view 2018-07-06 22:26:38 +02:00
Alexis de Lattre
dd15e3d194 Add search on supplier on product search view
Move margin fields to sale order line form view (instead of tree view)
2018-07-06 19:33:35 +02:00
Pierrick Brun
e3fc2764fb [MIG] 10.0 product_search_supplier_code 2018-04-26 14:55:26 +02:00
hparfr
f0bb02edf1 Add purchase buyer module 2018-04-11 10:49:41 +02:00
hparfr
dc05ed3b5d Migration to v10 2018-02-14 11:28:03 +01:00
chafique-delli
2c1b8fe657 Moves product_unit_manager_group from 8.0 2018-02-14 11:27:22 +01:00
95 changed files with 1595 additions and 263 deletions

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import account_invoice
from . import account_invoice_report

View File

@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api
class AccountInvoiceReport(models.Model):
_inherit = 'account.invoice.report'
margin = fields.Float(string='Margin', readonly=True)
# why digits=0 ??? Why is it like that in the native "account" module
user_currency_margin = fields.Float(
string="Margin", compute='_compute_user_currency_margin', digits=0)
_depends = {
'account.invoice': [
'account_id', 'amount_total_company_signed',
'commercial_partner_id', 'company_id',
'currency_id', 'date_due', 'date_invoice', 'fiscal_position_id',
'journal_id', 'partner_bank_id', 'partner_id', 'payment_term_id',
'residual', 'state', 'type', 'user_id',
],
'account.invoice.line': [
'account_id', 'invoice_id', 'price_subtotal', 'product_id',
'quantity', 'uom_id', 'account_analytic_id',
'margin_company_currency',
],
'product.product': ['product_tmpl_id'],
'product.template': ['categ_id'],
'product.uom': ['category_id', 'factor', 'name', 'uom_type'],
'res.currency.rate': ['currency_id', 'name'],
'res.partner': ['country_id'],
}
@api.depends('currency_id', 'date', 'margin')
def _compute_user_currency_margin(self):
context = dict(self._context or {})
user_currency_id = self.env.user.company_id.currency_id
currency_rate_id = self.env['res.currency.rate'].search([
('rate', '=', 1),
'|',
('company_id', '=', self.env.user.company_id.id),
('company_id', '=', False)], limit=1)
base_currency_id = currency_rate_id.currency_id
ctx = context.copy()
for record in self:
ctx['date'] = record.date
record.user_currency_margin = base_currency_id.with_context(
ctx).compute(record.margin, user_currency_id)
# TODO check for refunds
def _sub_select(self):
select_str = super(AccountInvoiceReport, self)._sub_select()
select_str += ", SUM(ail.margin_company_currency) AS margin"
return select_str
def _select(self):
select_str = super(AccountInvoiceReport, self)._select()
select_str += ", sub.margin AS margin"
return select_str

View File

@@ -12,17 +12,21 @@
<field name="model">account.invoice.line</field>
<field name="inherit_id" ref="account.view_invoice_line_form"/>
<field name="arch" type="xml">
<field name="discount" position="after">
<field name="company_id" position="after">
<field name="standard_price_company_currency"
groups="account.group_account_user"/>
groups="base.group_no_one"/>
<field name="standard_price_invoice_currency"
widget="monetary"
options="{'currency_field': 'currency_id'}"
groups="account.group_account_user"/>
groups="base.group_no_one"/>
<field name="margin_invoice_currency"
groups="account.group_account_user"/>
groups="base.group_no_one"/>
<field name="margin_company_currency"
groups="account.group_account_user"/>
groups="base.group_no_one"/>
<label for="margin_rate" groups="base.group_no_one"/>
<div name="margin_rate" groups="base.group_no_one">
<field name="margin_rate" class="oe_inline"/> %
</div>
</field>
</field>
</record>
@@ -34,18 +38,10 @@
<field name="arch" type="xml">
<field name="move_id" position="after">
<field name="margin_invoice_currency"
string="Margin" groups="account.group_account_user"/>
string="Margin" groups="base.group_no_one"/>
<field name="margin_company_currency"
groups="account.group_account_user"/>
groups="base.group_no_one"/>
</field>
<xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='price_subtotal']" position="after">
<field name="standard_price_invoice_currency" groups="base.group_no_one" widget="monetary" options="{'currency_field': 'currency_id'}"/>
<field name="standard_price_company_currency" groups="base.group_no_one" widget="monetary" options="{'currency_field': 'company_currency_id'}"/>
<field name="margin_invoice_currency" groups="base.group_no_one"/>
<field name="margin_company_currency" groups="base.group_no_one"/>
<field name="margin_rate" groups="base.group_no_one"/>
<field name="company_currency_id" invisible="1"/>
</xpath>
</field>
</record>

View File

@@ -1,41 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Account Invoice Margin Report module for Odoo
# Copyright (C) 2015 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Account Invoice Margin Report',
'version': '0.1',
'category': 'Accounting & Finance',
'license': 'AGPL-3',
'summary': 'Add margin measure in Invoices Analysis',
'description': """
This module adds the measure *Margin* in the Invoices Analysis pivot table. It is in a separate module because it depends on the module *bi_invoice_company_currency* (in which I re-wrote the Invoice Analysis pivot table).
This module has been written by Alexis de Lattre from Akretion
<alexis.delattre@akretion.com>.
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['account_invoice_margin', 'bi_invoice_company_currency'],
'data': [],
'installable': False,
}

View File

@@ -1,3 +0,0 @@
# -*- coding: utf-8 -*-
from . import invoice_report

View File

@@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Account Invoice Margin Report module for Odoo
# Copyright (C) 2015 Akretion (http://www.akretion.com/)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields
import openerp.addons.decimal_precision as dp
class AccountInvoiceReportBi(models.Model):
_inherit = "account.invoice.report.bi"
margin_company_currency = fields.Float(
string='Margin', readonly=True,
digits=dp.get_precision('Account'))
def _select(self):
select = super(AccountInvoiceReportBi, self)._select()
select += """
, sum(ail.margin_company_currency) AS margin_company_currency
"""
return select

View File

@@ -18,7 +18,7 @@ class AccountMoveLineFilterWizard(models.TransientModel):
account_reconcile = fields.Boolean(
related='account_id.reconcile', readonly=True)
reconcile = fields.Selection([
('unreconciled', 'Unreconciled'),
('unreconciled', 'Unreconciled or Partially Reconciled'),
('reconciled', 'Fully Reconciled'),
# ('partial_reconciled', 'Partially Reconciled'),
], string='Reconciliation Filter')

View File

@@ -19,7 +19,7 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['account'],
'depends': ['account', 'base_usability'],
'data': ['account_view.xml'],
'installable': True,
}

View File

@@ -42,5 +42,20 @@
</field>
</record>
<!-- ANALYTIC ACCOUNT -->
<record id="view_account_analytic_account_form" model="ir.ui.view">
<field name="name">account_no_analytic_tags.analytic.account.form</field>
<field name="model">account.analytic.account</field>
<field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>
<field name="arch" type="xml">
<field name="tag_ids" position="attributes">
<attribute name="invisible">1</attribute>
</field>
</field>
</record>
<record id="account.account_analytic_tag_menu" model="ir.ui.menu">
<field name="groups_id" eval="[(6, 0, [ref('base_usability.group_nobody')])]"/>
</record>
</odoo>

View File

@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from . import account
from . import account_invoice_report
from . import partner
from . import wizard

View File

@@ -31,12 +31,14 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
'website': 'http://www.akretion.com',
'depends': [
'account',
'base_view_inheritance_extension',
'base_usability', # needed only to access base_usability.group_nobody
# in v12, I may create a module only for group_nobody
],
'data': [
'account_view.xml',
'account_report.xml',
'account_invoice_report_view.xml',
'partner_view.xml',
'product_view.xml',
'wizard/account_invoice_mark_sent_view.xml',

View File

@@ -5,6 +5,7 @@
from odoo import models, fields, api, _
from odoo.tools import float_compare, float_is_zero
from odoo.tools.misc import formatLang
from odoo.exceptions import UserError, ValidationError
from odoo import SUPERUSER_ID
import logging
@@ -43,7 +44,6 @@ class AccountInvoice(models.Model):
compute='_compute_has_attachment',
search='_search_has_attachment', readonly=True)
@api.multi
def _compute_has_discount(self):
prec = self.env['decimal.precision'].precision_get('Discount')
for inv in self:
@@ -78,6 +78,25 @@ class AccountInvoice(models.Model):
res = [('id', value and 'in' or 'not in', att_inv_ids.keys())]
return res
# when you have an invoice created from a lot of sale orders, the 'name'
# field is very large, which makes the name_get() of that invoice very big
# which screws-up the form view of that invoice because of the link at the
# top of the screen
# That's why we have to cut the name_get() when it's too long
def name_get(self):
old_res = super(AccountInvoice, self).name_get()
res = []
for old_re in old_res:
name = old_re[1]
if name and len(name) > 100:
# nice cut
name = u'%s ...' % ', '.join(name.split(', ')[:3])
# if not enough, hard cut
if len(name) > 120:
name = u'%s ...' % old_re[1][:120]
res.append((old_re[0], name))
return res
# I really hate to see a "/" in the 'name' field of the account.move.line
# generated from customer invoices linked to the partners' account because:
# 1) the label of an account move line is an important field, we can't
@@ -102,6 +121,30 @@ class AccountInvoice(models.Model):
lines.unlink()
return True
def fix_invoice_attachment_filename(self):
# This script is designed to fix attachment of invoices
# badly generated by Odoo v8. I found this problem in Nov 2018 at
# Encres Dubuit when investigating a bug where Odoo would create a
# new attachment when printing an old invoice that already had the
# PDF of the invoice as attachment
logger.info('START fix customer invoice attachment filename')
# Run this script as admin to fix problem in all companies
self = self.sudo()
attachs = self.env['ir.attachment'].search([
('res_model', '=', 'account.invoice'),
('res_id', '!=', False),
('type', '=', 'binary'),
('name', '=like', 'INV%.pdf'),
('datas_fname', '=like', 'INV%.pdf.pdf')])
for attach in attachs:
inv = self.browse(attach.res_id)
if inv.type in ('out_invoice', 'out_refund'):
attach.datas_fname = attach.name
logger.info(
'Fixed field datas_fname of attachment ID %s name %s',
attach.id, attach.name)
logger.info('END fix customer invoice attachment filename')
class AccountInvoiceLine(models.Model):
_inherit = 'account.invoice.line'
@@ -114,7 +157,7 @@ class AccountInvoiceLine(models.Model):
related='invoice_id.date_invoice', store=True, readonly=True)
commercial_partner_id = fields.Many2one(
related='invoice_id.partner_id.commercial_partner_id',
store=True, readonly=True)
store=True, readonly=True, compute_sudo=True)
state = fields.Selection(
related='invoice_id.state', store=True, readonly=True,
string='Invoice State')
@@ -225,7 +268,7 @@ class AccountAccount(models.Model):
journal_accounts_bank_type += account
accounts = aao.search([
('user_type_id', '=', bank_type.id)], order='company_id, code')
for account in aao.search([('user_type_id', '=', bank_type.id)]):
for account in accounts:
if account not in journal_accounts_bank_type:
account.user_type_id = asset_type.id
logger.info(
@@ -234,6 +277,43 @@ class AccountAccount(models.Model):
logger.info("END of the script 'fix bank and cash account types'")
return True
@api.model
def create_account_groups(self, level=2, name_prefix=u'Comptes '):
'''Should be launched by a script. Make sure the account_group module is installed
(the account_usability module doesn't depend on it currently'''
assert level >= 1
assert isinstance(level, int)
companies = self.env['res.company'].search([])
if len(companies) > 1:
logger.info(
'Multi-company detected: running script create_account_groups '
'as admin')
self = self.sudo()
ago = self.env['account.group']
groups = ago.search([])
if groups:
raise UserError(_("Some account groups already exists"))
accounts = self.search([])
struct = {'childs': {}}
for account in accounts:
assert len(account.code) > level
n = 1
parent = struct
gparent = False
while n <= level:
group_code = account.code[:n]
if group_code not in parent['childs']:
new_group = ago.create({
'name': u'%s%s' % (name_prefix or '', group_code),
'code_prefix': group_code,
'parent_id': gparent and gparent.id or False,
})
parent['childs'][group_code] = {'obj': new_group, 'childs': {}}
parent = parent['childs'][group_code]
gparent = parent['obj']
n += 1
account.group_id = gparent.id
class AccountAnalyticAccount(models.Model):
_inherit = 'account.analytic.account'
@@ -243,9 +323,7 @@ class AccountAnalyticAccount(models.Model):
if self._context.get('analytic_account_show_code_only'):
res = []
for record in self:
res.append((
record.id,
record.code or record._get_one_full_name(record)))
res.append((record.id, record.code or record.name))
return res
else:
return super(AccountAnalyticAccount, self).name_get()
@@ -265,6 +343,29 @@ class AccountMove(models.Model):
# By default, we can still modify "ref" when account move is posted
# which seems a bit lazy for me...
ref = fields.Char(states={'posted': [('readonly', True)]})
date = fields.Date(copy=False)
default_account_id = fields.Many2one(
related='journal_id.default_debit_account_id', readonly=True)
default_credit = fields.Float(
compute='_compute_default_credit_debit', readonly=True)
default_debit = fields.Float(
compute='_compute_default_credit_debit', readonly=True)
@api.depends('line_ids.credit', 'line_ids.debit')
def _compute_default_credit_debit(self):
for move in self:
total_debit = total_credit = default_debit = default_credit = 0.0
for l in move.line_ids:
total_debit += l.debit
total_credit += l.credit
# I could use float_compare, but I don't think it's really needed
# in this context
if total_debit > total_credit:
default_credit = total_debit - total_credit
else:
default_debit = total_credit - total_debit
move.default_credit = default_credit
move.default_debit = default_debit
class AccountMoveLine(models.Model):
@@ -278,6 +379,14 @@ class AccountMoveLine(models.Model):
# Update field only to add a string (there is no string in account module)
invoice_id = fields.Many2one(string='Invoice')
date_maturity = fields.Date(copy=False)
account_reconcile = fields.Boolean(
related='account_id.reconcile', readonly=True)
full_reconcile_id = fields.Many2one(string='Full Reconcile')
matched_debit_ids = fields.One2many(string='Partial Reconcile Debit')
matched_credit_ids = fields.One2many(string='Partial Reconcile Credit')
reconcile_string = fields.Char(
compute='_compute_reconcile_string', string='Reconcile', store=True)
@api.onchange('credit')
def _credit_onchange(self):
@@ -315,7 +424,6 @@ class AccountMoveLine(models.Model):
else:
self.credit = amount_company_currency
@api.multi
def show_account_move_form(self):
self.ensure_one()
action = self.env['ir.actions.act_window'].for_xml_id(
@@ -328,6 +436,34 @@ class AccountMoveLine(models.Model):
})
return action
@api.depends(
'full_reconcile_id', 'matched_debit_ids', 'matched_credit_ids')
def _compute_reconcile_string(self):
for line in self:
rec_str = False
if line.full_reconcile_id:
rec_str = line.full_reconcile_id.name
else:
rec_str = ', '.join([
'a%d' % pr.id for pr in line.matched_debit_ids + line.matched_credit_ids])
line.reconcile_string = rec_str
class AccountPartialReconcile(models.Model):
_inherit = "account.partial.reconcile"
_rec_name = "id"
def name_get(self):
res = []
for rec in self:
# There is no seq for partial rec, so I simulate one with the ID
# Prefix for full rec: 'A' (upper case)
# Prefix for partial rec: 'a' (lower case)
amount_fmt = formatLang(self.env, rec.amount, currency_obj=rec.company_currency_id)
name = 'a%d (%s)' % (rec.id, amount_fmt)
res.append((rec.id, name))
return res
class AccountBankStatement(models.Model):
_inherit = 'account.bank.statement'
@@ -347,6 +483,16 @@ class AccountBankStatement(models.Model):
st.start_date = dates and min(dates) or False
st.end_date = dates and max(dates) or False
@api.multi
@api.depends('name', 'start_date', 'end_date')
def name_get(self):
res = []
for statement in self:
name = "%s (%s => %s)" % (
statement.name, statement.start_date, statement.end_date)
res.append((statement.id, name))
return res
class AccountBankStatementLine(models.Model):
_inherit = 'account.bank.statement.line'

View File

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields
class AccountInvoiceReport(models.Model):
_inherit = 'account.invoice.report'
number = fields.Char(string="Number", readonly=True)
def _sub_select(self):
select_str = super(AccountInvoiceReport, self)._sub_select()
select_str += ", ai.number"
return select_str
def _select(self):
select_str = super(AccountInvoiceReport, self)._select()
select_str += ", sub.number"
return select_str
def _group_by(self):
group_by_str = super(AccountInvoiceReport, self)._group_by()
group_by_str += ", ai.number"
return group_by_str

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Akretion (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="account_invoice_report_tree" model="ir.ui.view">
<field name="name">usability.account.invoice.report.tree</field>
<field name="model">account.invoice.report</field>
<field name="arch" type="xml">
<tree string="Invoices Analysis">
<field name="number"/>
<field name="date"/>
<field name="date_due"/>
<field name="type"/>
<field name="commercial_partner_id"/>
<field name="user_id"/>
<field name="product_id"/>
<field name="product_qty" sum="1"/>
<field name="uom_name" groups="product.group_uom"/>
<field name="price_total" sum="1"/>
<field name="state"/>
</tree>
</field>
</record>
<record id="account.action_account_invoice_report_all_supp" model="ir.actions.act_window">
<field name="context">{'search_default_current': 1, 'search_default_supplier': 1, 'search_default_year': 1}</field> <!-- Remove group_by_no_leaf, which breaks tree view -->
</record>
<record id="account.action_account_invoice_report_all" model="ir.actions.act_window">
<field name="context">{'search_default_current': 1, 'search_default_customer': 1, 'search_default_year': 1}</field> <!-- Remove group_by_no_leaf, which breaks tree view -->
</record>
<record id="view_account_invoice_report_pivot" model="ir.ui.view">
<field name="name">usability.account.invoice.report</field>
<field name="model">account.invoice.report</field>
<field name="inherit_id" ref="account.view_account_invoice_report_pivot"/>
<field name="arch" type="xml">
<pivot position="attributes">
<attribute name="disable_linking"></attribute>
</pivot>
</field>
</record>
</odoo>

View File

@@ -19,6 +19,9 @@
<field name="invoice_line_ids" position="before">
<button name="delete_lines_qty_zero" states="draft" string="⇒ Delete lines qty=0" type="object" class="oe_link oe_right" groups="account.group_account_invoice"/>
</field>
<xpath expr="//field[@name='tax_line_ids']/tree/field[@name='amount']" position="before">
<field name="base" readonly="1"/>
</xpath>
</field>
</record>
@@ -35,6 +38,16 @@
<field name="move_id" position="before">
<field name="sent"/>
</field>
<xpath expr="//field[@name='tax_line_ids']/tree/field[@name='amount']" position="before">
<field name="base" readonly="1"/>
</xpath>
<!-- Warning: there are 2 invoice_print buttons in the native view... probably a bug -->
<xpath expr="//button[@name='invoice_print']" position="attributes">
<attribute name="attrs">{'invisible': [('state', 'not in', ('open', 'paid'))]}</attribute>
</xpath>
<xpath expr="//button[@name='invoice_print'][2]" position="attributes">
<attribute name="attrs">{'invisible': True}</attribute>
</xpath>
</field>
</record>
@@ -241,6 +254,17 @@ module -->
</field>
</record>
<record id="view_account_payment_form" model="ir.ui.view">
<field name="name">usability.account.payment.form</field>
<field name="model">account.payment</field>
<field name="inherit_id" ref="account.view_account_payment_form"/>
<field name="arch" type="xml">
<field name="communication" position="after">
<field name="payment_reference"/>
</field>
</field>
</record>
<!-- model account.move.line / Journal Items -->
<record id="account.action_account_moves_all_a" model="ir.actions.act_window">
<field name="limit">200</field>
@@ -271,12 +295,18 @@ module -->
<field name="arch" type="xml">
<field name="ref" position="after">
<field name="default_move_line_name"/>
<field name="default_account_id" invisible="1"/>
<field name="default_credit" invisible="0"/>
<field name="default_debit" invisible="0"/>
</field>
<xpath expr="//field[@name='line_ids']" position="attributes">
<attribute name="context">{'line_ids': line_ids, 'journal_id': journal_id, 'default_name': default_move_line_name}</attribute>
<attribute name="context" operation="python_dict" key="default_name">default_move_line_name</attribute>
<attribute name="context" operation="python_dict" key="default_account_id">default_account_id</attribute>
<attribute name="context" operation="python_dict" key="default_credit">default_credit</attribute>
<attribute name="context" operation="python_dict" key="default_debit">default_debit</attribute>
</xpath>
<xpath expr="//field[@name='line_ids']/tree/field[@name='date_maturity']" position="after">
<field name="full_reconcile_id"/>
<xpath expr="//field[@name='line_ids']/tree/field[@name='credit']" position="after">
<field name="reconcile_string"/>
</xpath>
</field>
</record>
@@ -287,7 +317,8 @@ module -->
<field name="inherit_id" ref="account.view_account_move_line_filter"/>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="full_reconcile_id" />
<field name="reconcile_string" />
<field name="debit" filter_domain="['|', ('debit', '=', self), ('credit', '=', self)]" string="Debit or Credit"/>
</field>
<filter name="unreconciled" position="before">
<filter name="reconciled" string="Fully Reconciled" domain="[('full_reconcile_id', '!=', False)]"/>
@@ -299,6 +330,9 @@ module -->
<field name="name" position="attributes">
<attribute name="string">Name or Reference</attribute>
</field>
<field name="partner_id" position="attributes">
<attribute name="domain">['|', ('parent_id', '=', False), ('is_company', '=', True)]</attribute>
</field>
</field>
</record>
@@ -312,7 +346,21 @@ module -->
</field>
<field name="move_id" position="after">
<field name="invoice_id"/>
<field name="account_reconcile" invisible="1"/>
</field>
<xpath expr="//field[@name='full_reconcile_id']/.." position="replace">
<field name="full_reconcile_id" nolabel="1"/> <!-- label is already in view -->
<field name="matched_debit_ids" readonly="1" widget="many2many_tags" attrs="{'invisible': ['|', ('full_reconcile_id', '!=', False), ('matched_debit_ids', '=', [])]}"/>
<field name="matched_credit_ids" readonly="1" widget="many2many_tags" attrs="{'invisible': ['|', ('full_reconcile_id', '!=', False), ('matched_credit_ids', '=', [])]}"/>
<field name="reconciled" invisible="1"/>
<button name="open_reconcile_view" class="oe_link" type="object"
string="-> View partially reconciled entries" colspan="2"
attrs="{'invisible': ['|', ('full_reconcile_id', '!=', False), '&amp;', ('matched_debit_ids', '=', []),('matched_credit_ids', '=', [])]}"/>
<span colspan="2" attrs="{'invisible': ['|', '|', ('full_reconcile_id', '!=', False), ('matched_debit_ids', '!=', []), ('matched_credit_ids', '!=', [])]}" class="o_form_field">No Partial Reconcile</span>
</xpath>
<xpath expr="//label[@for='full_reconcile_id']/.." position="attributes">
<attribute name="attrs">{'invisible': [('account_reconcile', '=', False)]}</attribute>
</xpath>
</field>
</record>
@@ -324,7 +372,8 @@ module -->
<!-- Move reconcile_id to a better position -->
<field name="full_reconcile_id" position="replace"/>
<field name="credit" position="after">
<field name="full_reconcile_id"/>
<field name="balance" sum="Total Balance"/>
<field name="reconcile_string"/>
</field>
<field name="date_maturity" position="after">
<button name="show_account_move_form" type="object" icon="fa-arrows-h" string="Show Journal Entry"/>
@@ -332,6 +381,17 @@ module -->
</field>
</record>
<record id="view_account_move_filter" model="ir.ui.view">
<field name="name">account_usability.account_move_search</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_account_move_filter"/>
<field name="arch" type="xml">
<field name="partner_id" position="attributes">
<attribute name="domain">['|', ('parent_id', '=', False), ('is_company', '=', True)]</attribute>
</field>
</field>
</record>
<record id="view_account_search" model="ir.ui.view">
<field name="name">account.account.search</field>
<field name="model">account.account</field>
@@ -375,6 +435,9 @@ module -->
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_form"/>
<field name="arch" type="xml">
<button name="button_cancel" position="attributes">
<attribute name="invisible">1</attribute>
</button>
<xpath expr="//field[@name='line_ids']/tree/field[@name='bank_account_id']" position="after">
<!-- The cancel button is provided by the account_cancel module, but we don't want to depend on it -->
<button name="show_account_move" type="object"
@@ -471,6 +534,11 @@ because it is useless and confusing -->
<field name="groups_id" eval="[(6, 0, [ref('base_usability.group_nobody')])]"/>
</record>
<!-- Remove menu entry "Accounting > Reports > PDF Reports" as there are broken -->
<record id="account.menu_finance_legal_statement" model="ir.ui.menu">
<field name="groups_id" eval="[(6, 0, [ref('base_usability.group_nobody')])]"/>
</record>
<!-- Duplicate the menu "Sales > Configuration > Contacts > Bank Accounts"
under "Accounting > Configuration", because most users will try to find it there -->
<menuitem id="bank_account_account_config_menu" name="Bank Accounts" parent="account.menu_finance_configuration" sequence="9"/>

View File

@@ -0,0 +1,33 @@
diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py
index b1d8012329d..b8a8e2a673d 100644
--- a/addons/account/models/account_payment.py
+++ b/addons/account/models/account_payment.py
@@ -210,6 +210,7 @@ class account_payment(models.Model):
payment_difference = fields.Monetary(compute='_compute_payment_difference', readonly=True)
payment_difference_handling = fields.Selection([('open', 'Keep open'), ('reconcile', 'Mark invoice as fully paid')], default='open', string="Payment Difference", copy=False)
writeoff_account_id = fields.Many2one('account.account', string="Difference Account", domain=[('deprecated', '=', False)], copy=False)
+ writeoff_analytic_account_id = fields.Many2one('account.analytic.account', string="Difference Analytic Account", copy=False)
# FIXME: ondelete='restrict' not working (eg. cancel a bank statement reconciliation with a payment)
move_line_ids = fields.One2many('account.move.line', 'payment_id', readonly=True, copy=False, ondelete='restrict')
@@ -431,6 +432,7 @@ class account_payment(models.Model):
amount_currency_wo = -abs(amount_currency_wo)
writeoff_line['name'] = _('Counterpart')
writeoff_line['account_id'] = self.writeoff_account_id.id
+ writeoff_line['analytic_account_id'] = self.writeoff_analytic_account_id.id or False
writeoff_line['debit'] = debit_wo
writeoff_line['credit'] = credit_wo
writeoff_line['amount_currency'] = amount_currency_wo
diff --git a/addons/account/views/account_payment_view.xml b/addons/account/views/account_payment_view.xml
index 2460458fbaa..4065d8f9952 100644
--- a/addons/account/views/account_payment_view.xml
+++ b/addons/account/views/account_payment_view.xml
@@ -206,6 +206,8 @@
</div>
<field name="writeoff_account_id" string="Post Difference In"
attrs="{'invisible': [('payment_difference_handling','=','open')], 'required': [('payment_difference_handling', '=', 'reconcile')]}"/>
+ <field name="writeoff_analytic_account_id" string="Post Difference In Analytic Account"
+ attrs="{'invisible': [('payment_difference_handling','=','open')]}"/>
</group>
</group>
</sheet>

View File

@@ -31,6 +31,7 @@ A group by 'State' is added to module search view.
'security/group.xml',
'security/ir.model.access.csv',
'partner_view.xml',
'partner_bank_view.xml',
'users_view.xml',
'country_view.xml',
'module_view.xml',

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Akretion (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_partner_bank_tree" model="ir.ui.view">
<field name="name">base_usability.res.partner.bank.tree</field>
<field name="model">res.partner.bank</field>
<field name="inherit_id" ref="base.view_partner_bank_tree"/>
<field name="arch" type="xml">
<field name="sequence" position="attributes">
<attribute name="invisible">0</attribute>
<attribute name="widget">handle</attribute>
</field>
</field>
</record>
</odoo>

View File

@@ -19,6 +19,10 @@
<xpath expr="//field[@name='child_ids']/form//field[@name='email']" position="attributes">
<attribute name="widget">email</attribute>
</xpath>
<!-- Show title not only on Contacts -->
<xpath expr="//field[@name='child_ids']/form//field[@name='title']" position="attributes">
<attribute name="attrs"></attribute>
</xpath>
</field>
</record>

View File

@@ -22,6 +22,7 @@ def formatLang(
if (
'base.usability.installed' in env and
int_no_digits and
not monetary and
isinstance(value, float) and
dp):
prec = env['decimal.precision'].precision_get(dp)
@@ -33,4 +34,5 @@ def formatLang(
grouping=grouping, monetary=monetary, dp=dp, currency_obj=currency_obj)
return res
report_sxw.rml_parse.formatLang = formatLang

View File

@@ -0,0 +1,37 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
========================
User Authentication Logs
========================
This module adds user authentication logs in Odoo. It logs both authentication success and failures.
Usage
=====
The authentication logs can be seen:
* on the users's form view in the *Auth Logs* tab,
* in the menu *Settings > Technical > Security > Authentication Logs*.
Authentication failure logs are displayed in red. Authentication success logs are displayed in black.
To have read access to the logs, you need to be part of the *Access Rights* group.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/akretion/odoo-usability/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Alexis de Lattre <alexis.delattre@akretion.com>

View File

@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
from . import report
from . import models

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Copyright 2017-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Users authentification logs',
'version': '10.0.1.0.0',
'category': 'Tools',
'license': 'AGPL-3',
'summary': 'Adds users authentication logs',
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['base'],
'data': [
'security/ir.model.access.csv',
'views/res_users_auth_log.xml',
'views/res_users.xml',
'data/ir_cron.xml',
],
'installable': True,
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo noupdate="1">
<record id="purge_auth_log_cron" model="ir.cron">
<field name="name">Purge old authentication logs</field>
<field name="active" eval="True"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">months</field>
<field name="numbercall">-1</field> <!-- don't limit the number of calls -->
<field name="doall" eval="False"/>
<field name="model" eval="'res.users.auth.log'"/>
<field name="function" eval="'_purge_old_auth_logs'" />
<field name="args" eval="'()'"/>
</record>
</odoo>

View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from . import res_users_auth_log
from . import res_users

View File

@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# Copyright 2017-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, registry, SUPERUSER_ID
import logging
logger = logging.getLogger(__name__)
class ResUsers(models.Model):
_inherit = 'res.users'
auth_log_ids = fields.One2many(
'res.users.auth.log', 'user_id', string='Authentication Logs')
@classmethod
def _login(cls, db, login, password):
user_id = super(ResUsers, cls)._login(db, login, password)
with registry(db).cursor() as cr:
if user_id:
result = 'success'
user_log_id = user_id
else:
# To write a null value, psycopg2 wants None
user_log_id = None
result = 'failure'
cr.execute(
"SELECT id FROM res_users WHERE login=%s", (login, ))
user_select = cr.fetchall()
if user_select:
user_log_id = user_select[0][0]
cr.execute("""
INSERT INTO res_users_auth_log (
create_uid,
create_date,
date,
login,
result,
user_id
) VALUES (
%s, NOW() AT TIME ZONE 'UTC', NOW() AT TIME ZONE 'UTC',
%s, %s, %s)""", (SUPERUSER_ID, login, result, user_log_id))
logger.info('Auth log created for login %s type %s', login, result)
return user_id

View File

@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# © 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api, _
from odoo.exceptions import UserError
from datetime import datetime, timedelta
import logging
logger = logging.getLogger(__name__)
class ResUsersAuthLog(models.Model):
_name = 'res.users.auth.log'
_description = 'Users Authentication Logs'
_order = 'date desc'
_rec_name = 'date'
user_id = fields.Many2one(
'res.users', string='User', ondelete='cascade', readonly=True)
login = fields.Char(string='Login', readonly=True)
date = fields.Datetime(
string='Authentication Date', required=True, readonly=True)
result = fields.Selection([
('success', 'Success'),
('failure', 'Failure'),
], string='Result', required=True, readonly=True)
@api.model
def create(self, vals):
if not self._context.get('authenticate_create'):
raise UserError(_(
"You cannot manually create an authentication log."))
return super(ResUsersAuthLog, self).create(vals)
@api.multi
def write(self, vals):
raise UserError(_("You cannot modify an authentication log."))
@api.model
def _purge_old_auth_logs(self):
expiry_date = datetime.today() - timedelta(days=365)
self._cr.execute(
"DELETE FROM res_users_auth_log WHERE date <= %s", (expiry_date, ))
logger.info('Auth logs older than %s have been purged', expiry_date)

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_res_users_auth_log,Read access to Access rights group,model_res_users_auth_log,base.group_erp_manager,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_res_users_auth_log Read access to Access rights group model_res_users_auth_log base.group_erp_manager 1 0 0 0

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_users_form" model="ir.ui.view">
<field name="name">auth_logs.res.users.form</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<notebook position="inside">
<page string="Auth Logs" name="auth_logs">
<field name="auth_log_ids" nolabel="1"/>
</page>
</notebook>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="res_users_auth_log_form" model="ir.ui.view">
<field name="name">res.users.auth.logs.form</field>
<field name="model">res.users.auth.log</field>
<field name="arch" type="xml">
<form string="Authentication Log">
<group name="main">
<field name="date"/>
<field name="user_id"/>
<field name="login"/>
<field name="result"/>
</group>
</form>
</field>
</record>
<record id="res_users_auth_log_tree" model="ir.ui.view">
<field name="name">res.users.auth.logs.tree</field>
<field name="model">res.users.auth.log</field>
<field name="arch" type="xml">
<tree string="Authentication Logs" colors="red:result=='failure'">
<field name="date"/>
<field name="user_id" invisible="not context.get('auth_logs_main_view')"/>
<field name="login" invisible="not context.get('auth_logs_main_view')"/>
<field name="result"/>
</tree>
</field>
</record>
<record id="res_users_auth_log_search" model="ir.ui.view">
<field name="name">res.users.auth.logs.search</field>
<field name="model">res.users.auth.log</field>
<field name="arch" type="xml">
<search string="Search Authentication Logs">
<field name="user_id"/>
<filter name="success" string="Success" domain="[('result', '=', 'success')]"/>
<filter name="failure" string="Failure" domain="[('result', '=', 'failure')]"/>
<group string="Group By" name="groupby">
<filter name="day_groupby" string="Day" context="{'group_by': 'date:day'}"/>
<filter name="week_groupby" string="Week" context="{'group_by': 'date:week'}"/>
<filter name="month_groupby" string="Month" context="{'group_by': 'date:month'}"/>
<filter name="user_groupby" string="User" context="{'group_by': 'user_id'}"/>
<filter name="result_groupby" string="Result" context="{'group_by': 'result'}"/>
</group>
</search>
</field>
</record>
<record id="res_users_auth_log_graph" model="ir.ui.view">
<field name="name">res.users.auth.logs.graph</field>
<field name="model">res.users.auth.log</field>
<field name="arch" type="xml">
<graph string="Analyze Authentication Logs" type="pivot">
<field name="date" type="row" interval="week"/>
<field name="user_id" type="col"/>
</graph>
</field>
</record>
<record id="res_users_auth_log_action" model="ir.actions.act_window">
<field name="name">Authentication Logs</field>
<field name="res_model">res.users.auth.log</field>
<field name="view_mode">tree,form,graph</field>
<field name="context">{'auth_logs_main_view': True}</field>
</record>
<menuitem id="res_users_auth_log_menu" action="res_users_auth_log_action"
parent="base.menu_security" sequence="100"/>
</odoo>

View File

@@ -1,2 +1,3 @@
# -*- coding: utf-8 -*-
from . import stock

View File

@@ -24,6 +24,7 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
'data': [
'delivery_view.xml',
'sale_view.xml',
'stock_view.xml',
],
'installable': True,
}

View File

@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class StockPicking(models.Model):
_inherit = 'stock.picking'
carrier_id = fields.Many2one(track_visibility='onchange')

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_picking_withcarrier_out_form" model="ir.ui.view">
<field name="name">delivery_usability.stock.picking.form</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="delivery.view_picking_withcarrier_out_form"/>
<field name="arch" type="xml">
<field name="carrier_id" position="attributes">
<!-- Sometimes we have to modify carrier_id when state is done
so remove readonly when state = done from view and add tracking on_change in
field definition -->
<attribute name="attrs">{}</attribute>
</field>
</field>
</record>
</odoo>

View File

@@ -103,7 +103,7 @@ class HrExpense(models.Model):
string='Untaxed Amount', currency_field='currency_id',
readonly=True, states={'draft': [('readonly', False)]})
company_currency_id = fields.Many2one(
related='company_id.currency_id', readonly=True, store=True)
related='company_id.currency_id', readonly=True, store=True, compute_sudo=True)
total_amount_company_currency = fields.Monetary(
compute='compute_amount_company_currency', readonly=True,
store=True, string='Total in Company Currency',
@@ -309,7 +309,7 @@ class HrExpenseSheet(models.Model):
responsible_id = fields.Many2one(track_visibility='onchange')
accounting_date = fields.Date(track_visibility='onchange')
company_currency_id = fields.Many2one(
related='company_id.currency_id', readonly=True, store=True)
related='company_id.currency_id', readonly=True, store=True, compute_sudo=True)
total_amount_company_currency = fields.Monetary(
compute='compute_total_company_currency',
currency_field='company_currency_id', readonly=True, store=True,

View File

@@ -9,7 +9,7 @@ class PurchaseConfigSettings(models.TransientModel):
_inherit = 'purchase.config.settings'
lunch_voucher_product_id = fields.Many2one(
related='company_id.lunch_voucher_product_id')
related='company_id.lunch_voucher_product_id', compute_sudo=True)
lunch_voucher_employer_price = fields.Monetary(
related='company_id.lunch_voucher_employer_price',
currency_field='company_currency_id')
currency_field='company_currency_id', compute_sudo=True)

View File

@@ -10,6 +10,6 @@ class PurchaseConfigSettings(models.TransientModel):
_inherit = 'purchase.config.settings'
lunch_voucher_natixis_customer_code = fields.Char(
related='company_id.lunch_voucher_natixis_customer_code')
related='company_id.lunch_voucher_natixis_customer_code', compute_sudo=True)
lunch_voucher_natixis_delivery_code = fields.Char(
related='company_id.lunch_voucher_natixis_delivery_code')
related='company_id.lunch_voucher_natixis_delivery_code', compute_sudo=True)

View File

@@ -223,7 +223,7 @@ class HrHolidays(models.Model):
readonly=True)
limit = fields.Boolean( # pose des pbs de droits
related='holiday_status_id.limit', string='Allow to Override Limit',
readonly=True)
readonly=True, compute_sudo=True)
payslip_date = fields.Date(
string='Transfer to Payslip Date', track_visibility='onchange',
readonly=True)
@@ -245,7 +245,7 @@ class HrHolidays(models.Model):
# by default, there is no company_id field on hr.holidays !
company_id = fields.Many2one(
related='employee_id.resource_id.company_id', store=True,
readonly=True)
readonly=True, compute_sudo=True)
state = fields.Selection(default='draft') # hr_holidays, default='confirm'
@api.constrains(
@@ -441,4 +441,4 @@ class BaseConfigSettings(models.TransientModel):
_inherit = 'base.config.settings'
mass_allocation_default_holiday_status_id = fields.Many2one(
related='company_id.mass_allocation_default_holiday_status_id')
related='company_id.mass_allocation_default_holiday_status_id', compute_sudo=True)

20
mail_usability/README.rst Normal file
View File

@@ -0,0 +1,20 @@
# Mail Usability
Take back the control on your email
## Feature
- do not follow automatically a object when sending an email
- add the option 'All Messages Except Notifications' on partner and use it by default to avoid sending unwanted mail to partner
- better email preview, allow to select between the whole database object and not only the last 10
- use a light template version for notification without link (link should be explicit)
- add some additional style in the white list when santizing html field (see tools.py)
- make the email template by default not 'auto_delete'
## TIPS
Never, never tick the 'auto_delete' on mail template because it fucking hard to debug
and understand what have been sent (we should create a module with a crontask, that drop them latter)
If the template of mail do not look like the same when saving it in odoo, maybe the sanitize style have drop some balise
please run odoo with "LOG_STYLE_SANITIZE=True odoo" to understand what have been drop, magic warning logger will tell you everthing

View File

@@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
from . import mail
from . import tools
from . import mail_template

View File

@@ -25,6 +25,9 @@ Small usability improvements on mails:
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['mail'],
'data': ['mail_view.xml'],
'data': [
'mail_view.xml',
'mail_data.xml',
],
'installable': True,
}

View File

@@ -15,29 +15,94 @@ class ResPartner(models.Model):
('all_except_notification', 'All Messages Except Notifications')],
default='all_except_notification')
@api.multi
def _notify(
self, message, force_send=False, send_after_commit=True,
user_signature=True):
def _should_be_notify_by_email(self, message):
if message.message_type == 'notification':
message_sudo = message.sudo()
email_channels = message.channel_ids.filtered(
lambda channel: channel.email_send)
bad_email = message_sudo.author_id and\
message_sudo.author_id.email or message.email_from
self.sudo().search([
'|',
('id', 'in', self.ids),
('channel_ids', 'in', email_channels.ids),
('email', '!=', bad_email),
('notify_email', '=', 'always')])._notify_by_email(
message, force_send=force_send,
send_after_commit=send_after_commit,
user_signature=user_signature)
self._notify_by_chat(message)
return True
if self.notify_email == 'always':
return True
else:
return False
else:
return super(ResPartner, self)._notify(
message, force_send=force_send,
send_after_commit=send_after_commit,
user_signature=user_signature)
return True
def _notify_by_email(
self, message, force_send=False, send_after_commit=True,
user_signature=True):
# use an empty layout for notification by default
if not self._context.get('custom_layout'):
self = self.with_context(
custom_layout='mail_usability.mail_template_notification')
# Filter the partner that should receive the notification
filtered_partners = self.filtered(
lambda p: p._should_be_notify_by_email(message)
)
return super(ResPartner, filtered_partners)._notify_by_email(
message, force_send=force_send,
send_after_commit=send_after_commit,
user_signature=user_signature)
def _notify_prepare_email_values(self, message):
res = super(ResPartner, self)._notify_prepare_email_values(message)
# Never auto delete notification email
# fucking to hard to debug when message have been delete
res['auto_delete'] = False
return res
class TemplatePreview(models.TransientModel):
_inherit = "email_template.preview"
res_id = fields.Integer(compute='_compute_res_id')
object_id = fields.Reference(selection='_reference_models')
@api.model
def default_get(self, fields):
result = super(TemplatePreview, self).default_get(fields)
if result.get('model_id'):
model = self.env['ir.model'].browse(result['model_id'])
result['object_id'] = model.model
return result
def _reference_models(self):
result = self.default_get(['model_id'])
if result.get('model_id'):
model = self.env['ir.model'].browse(result['model_id'])
return [(model.model, model.name)]
else:
models = self.env['ir.model'].search([('state', '!=', 'manual')])
return [(model.model, model.name)
for model in models
if not model.model.startswith('ir.')]
@api.depends('object_id')
def _compute_res_id(self):
for record in self:
if self.object_id:
record.res_id = self.object_id.id
def send(self):
template = self.env['mail.template'].browse(
self._context['template_id'])
template.send_mail(
self.res_id, force_send=True, raise_exception=True)
class MailThread(models.AbstractModel):
_inherit = 'mail.thread'
@api.multi
@api.returns('self', lambda value: value.id)
def message_post(self, body='', subject=None, message_type='notification',
subtype=None, parent_id=False, attachments=None,
content_subtype='html', **kwargs):
if not 'mail_create_nosubscribe' in self._context:
# Do not implicitly follow an object by just sending a message
self = self.with_context(mail_create_nosubscribe=True)
return super(MailThread,
self.with_context(mail_create_nosubscribe=True)
).message_post(
body=body, subject=subject, message_type=message_type,
subtype=subtype, parent_id=parent_id, attachments=attachments,
content_subtype=content_subtype, **kwargs)

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!--Default Notification Email template -->
<record id="mail_template_notification" model="mail.template">
<field name="name">Notification Email</field>
<field name="subject">${object.subject}</field>
<field name="model_id" ref="mail.model_mail_message"/>
<field name="auto_delete" eval="True"/>
<field name="body_html">${object.body | safe}</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import fields, models
class MailTemplate(models.Model):
_inherit = 'mail.template'
auto_delete = fields.Boolean(default=False)

View File

@@ -19,4 +19,23 @@
</field>
</record>
<record id="email_template_preview_form" model="ir.ui.view">
<field name="model">email_template.preview</field>
<field name="inherit_id" ref="mail.email_template_preview_form"/>
<field name="arch" type="xml">
<field name="res_id" position="attributes">
<attribute name="invisible">True</attribute>
</field>
<field name="res_id" position="after">
<field name="object_id"/>
</field>
<footer position="inside">
<button
string="Send"
name="send"
class="btn-primary"
type='object'/>
</footer>
</field>
</record>
</odoo>

45
mail_usability/tools.py Normal file
View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.tools.mail import _Cleaner
import os
import logging
_logger = logging.getLogger(__name__)
_Cleaner._style_whitelist += [
'word-wrap',
'display'
'border-top',
'border-bottom',
'border-left',
'border-right',
'text-transform',
]
if os.getenv('LOG_STYLE_SANITIZE'):
# Monkey patch the parse style method to debug
# the missing style
def parse_style(self, el):
attributes = el.attrib
styling = attributes.get('style')
if styling:
valid_styles = {}
styles = self._style_re.findall(styling)
for style in styles:
if style[0].lower() in self._style_whitelist:
valid_styles[style[0].lower()] = style[1]
# START HACK
else:
_logger.warning('Remove style %s %s', *style)
# END HACK
if valid_styles:
el.attrib['style'] = '; '.join(
'%s:%s' % (key, val)
for (key, val) in valid_styles.iteritems())
else:
del el.attrib['style']
import pdb; pdb.set_trace()
_Cleaner.parse_style = parse_style

View File

@@ -13,6 +13,12 @@
<field name="model">mrp.production</field>
<field name="inherit_id" ref="mrp.mrp_production_form_view"/>
<field name="arch" type="xml">
<xpath expr="//page/group/group[@groups='stock.group_stock_multi_locations']/field[@name='location_src_id']" position="replace"/>
<xpath expr="//page/group/group[@groups='stock.group_stock_multi_locations']/field[@name='location_dest_id']" position="replace"/>
<field name="routing_id" position="after">
<field name="location_src_id" domain="[('usage','=','internal')]" attrs="{'readonly': [('has_moves', '=', True)]}" groups="stock.group_stock_multi_locations"/>
<field name="location_dest_id" domain="[('usage','=','internal')]" attrs="{'readonly': [('has_moves', '=', True)]}" groups="stock.group_stock_multi_locations"/>
</field>
<field name="availability" position="after">
<field name="date_start"/>
<field name="date_finished"/>

View File

@@ -16,6 +16,6 @@
'views/product_view.xml',
'views/picking_view.xml',
],
'installable': False,
'installable': True,
'active': False,
}

View File

@@ -1,13 +1,13 @@
# Translation of OpenERP Server.
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * product_search_supplier_code
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-04 15:18+0000\n"
"PO-Revision-Date: 2015-11-04 15:18+0000\n"
"PO-Revision-Date: 2018-04-26 15:18+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@@ -17,6 +17,7 @@ msgstr ""
#. module: product_search_supplier_code
#: view:product.product:0
#: view:product.template:0
#: view:stock.picking.in:0
msgid "Supplier Default Code"
msgstr "Code Fournisseur"

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<record id="stock_picking_supplier_code_search" model="ir.ui.view">
<field name="model">stock.picking</field>
@@ -13,5 +12,4 @@
</field>
</record>
</data>
</openerp>
</odoo>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<record id="product_custom_search_view" model="ir.ui.view">
<field name="model">product.product</field>
@@ -13,5 +12,15 @@
</field>
</record>
</data>
</openerp>
<record id="product_custom_search_view" model="ir.ui.view">
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="seller_ids" string="Supplier Default Code"
filter_domain="[('seller_ids.product_code','ilike',self)]" />
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
# © 2017 Chafique DELLI @ Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# © 2017 Chafique DELLI @ Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'Product Unit Manager Group',
'summary': 'Add a group Product Unit of Measure Manager',
'version': '10.0.1.0.1',
'category': 'Product',
'description': 'Splits the use from the mangement for uom rights',
'website': 'https://akretion.com',
'author': 'Akretion',
'license': 'AGPL-3',
'installable': True,
'depends': [
'sale',
'purchase',
'mrp',
],
'data': [
'security/product_security.xml',
'security/ir.model.access.csv',
'views/product_view.xml',
],
}

View File

@@ -0,0 +1,29 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * product_unit_manager_group
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-13 15:35+0000\n"
"PO-Revision-Date: 2017-03-13 15:35+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: product_unit_manager_group
#: model:res.groups,comment:product_unit_manager_group.group_uom_manager
#: model:res.groups,name:product_unit_manager_group.group_uom_manager
msgid "Manage Multiple Units of Measure"
msgstr "Gérer plusieurs unités de mesure"
#. module: product_unit_manager_group
#: model:res.groups,comment:product.group_uom
#: model:res.groups,name:product.group_uom
msgid "Use Multiple Units of Measure"
msgstr "Utiliser plusieurs unités de mesure"

View File

@@ -0,0 +1,13 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_product_uom_manager,product.uom.manager,product.model_product_uom,group_uom_manager,1,1,1,1
access_product_uom_categ_manager,product.uom.categ.manager,product.model_product_uom_categ,group_uom_manager,1,1,1,1
access_product_uom_user,product.uom.user,product.model_product_uom,product.group_uom,1,0,0,0
access_product_uom_categ_user,product.uom.categ.user,product.model_product_uom_categ,product.group_uom,1,0,0,0
sale.access_product_uom_sale_manager,product.uom salemanager,product.model_product_uom,sales_team.group_sale_manager,1,0,0,0
sale.access_product_uom_categ_sale_manager,product.uom.categ salemanager,product.model_product_uom_categ,sales_team.group_sale_manager,1,0,0,0
purchase.access_product_uom_purchase_manager,product.uom purchase_manager,product.model_product_uom,purchase.group_purchase_manager,1,0,0,0
purchase.access_product_uom_categ_purchase_manager,product.uom.categ purchase_manager,product.model_product_uom_categ,purchase.group_purchase_manager,1,0,0,0
mrp.access_product_uom_mrp_manager,product.uom mrp_manager,product.model_product_uom,mrp.group_mrp_manager,1,0,0,0
mrp.access_product_uom_categ_mrp_manager,product.uom.categ mrp_manager,product.model_product_uom_categ,mrp.group_mrp_manager,1,0,0,0
stock.access_product_uom_stock_manager,product.uom stock_manager,product.model_product_uom,stock.group_stock_manager,1,0,0,0
stock.access_product_uom_categ_stock_manager,product.uom.categ stock_manager,product.model_product_uom_categ,stock.group_stock_manager,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_product_uom_manager product.uom.manager product.model_product_uom group_uom_manager 1 1 1 1
3 access_product_uom_categ_manager product.uom.categ.manager product.model_product_uom_categ group_uom_manager 1 1 1 1
4 access_product_uom_user product.uom.user product.model_product_uom product.group_uom 1 0 0 0
5 access_product_uom_categ_user product.uom.categ.user product.model_product_uom_categ product.group_uom 1 0 0 0
6 sale.access_product_uom_sale_manager product.uom salemanager product.model_product_uom sales_team.group_sale_manager 1 0 0 0
7 sale.access_product_uom_categ_sale_manager product.uom.categ salemanager product.model_product_uom_categ sales_team.group_sale_manager 1 0 0 0
8 purchase.access_product_uom_purchase_manager product.uom purchase_manager product.model_product_uom purchase.group_purchase_manager 1 0 0 0
9 purchase.access_product_uom_categ_purchase_manager product.uom.categ purchase_manager product.model_product_uom_categ purchase.group_purchase_manager 1 0 0 0
10 mrp.access_product_uom_mrp_manager product.uom mrp_manager product.model_product_uom mrp.group_mrp_manager 1 0 0 0
11 mrp.access_product_uom_categ_mrp_manager product.uom.categ mrp_manager product.model_product_uom_categ mrp.group_mrp_manager 1 0 0 0
12 stock.access_product_uom_stock_manager product.uom stock_manager product.model_product_uom stock.group_stock_manager 1 0 0 0
13 stock.access_product_uom_categ_stock_manager product.uom.categ stock_manager product.model_product_uom_categ stock.group_stock_manager 1 0 0 0

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record id="product.group_uom" model="res.groups">
<field name="name">Use Multiple Units of Measure</field>
<field name="comment">Use multiple units of measure</field>
</record>
<record id="group_uom_manager" model="res.groups">
<field name="name">Manage Multiple Units of Measure (Manager)</field>
<field name="comment">Manage Multiple Units of Measure</field>
<field name="category_id" ref="base.module_category_hidden"/>
<field name="users" eval="[(4, ref('base.user_root'))]"/>
<field name="implied_ids" eval="[(6, 0, [ref('product.group_uom')])]"/>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="sale.next_id_16" model="ir.ui.menu">
<field name="groups_id" eval="[(6, 0, [ref('product_unit_manager_group.group_uom_manager')])]"/>
</record>
<record id="sale.menu_product_uom_form_action" model="ir.ui.menu">
<field name="groups_id" eval="[(6, 0, [ref('product_unit_manager_group.group_uom_manager')])]"/>
</record>
<record id="sale.menu_product_uom_categ_form_action" model="ir.ui.menu">
<field name="groups_id" eval="[(6, 0, [ref('product_unit_manager_group.group_uom_manager')])]"/>
</record>
<record id="stock.menu_stock_unit_measure_stock" model="ir.ui.menu">
<field name="groups_id" eval="[(6, 0, [ref('product_unit_manager_group.group_uom_manager')])]"/>
</record>
<record id="stock.menu_stock_uom_categ_form_action" model="ir.ui.menu">
<field name="groups_id" eval="[(6, 0, [ref('product_unit_manager_group.group_uom_manager')])]"/>
</record>
<record id="stock.menu_stock_uom_form_action" model="ir.ui.menu">
<field name="groups_id" eval="[(6, 0, [ref('product_unit_manager_group.group_uom_manager')])]"/>
</record>
<record id="purchase.menu_purchase_uom_categ_form_action" model="ir.ui.menu">
<field name="groups_id" eval="[(6, 0, [ref('product_unit_manager_group.group_uom_manager')])]"/>
</record>
<record id="purchase.menu_purchase_uom_form_action" model="ir.ui.menu">
<field name="groups_id" eval="[(6, 0, [ref('product_unit_manager_group.group_uom_manager')])]"/>
</record>
</data>
</odoo>

View File

@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
# © 2015-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields
@@ -70,5 +69,5 @@ class ProductPriceHistory(models.Model):
_inherit = 'product.price.history'
company_currency_id = fields.Many2one(
related='company_id.currency_id', readonly=True,
related='company_id.currency_id', readonly=True, compute_sudo=True,
string='Company Currency')

View File

@@ -88,11 +88,15 @@
</field>
</record>
<!-- It also adds on product.product search view -->
<record id="product_template_search_view" model="ir.ui.view">
<field name="name">usability.product.template.search</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view" />
<field name="arch" type="xml">
<field name="categ_id" position="after">
<field name="seller_ids" string="Supplier" filter_domain="[('seller_ids.name', 'ilike', self)]"/>
</field>
<field name="pricelist_id" position="after">
<group string="Group By" name="groupby">
<filter name="categ_groupby" string="Internal Category" context="{'group_by': 'categ_id'}"/>

View File

@@ -0,0 +1,31 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
====================
Purchase Order Buyer
====================
Adds a buyer on the Purchase Order. (Like a vendor on sales).
A prefered buyer can be set on the supplier.
Usage
=====
To use this module, you need to go to Purchase > Purchase Order: there a new field "Buyer"
It will use by default the user_id of the supplier and fallback to current user.
Credits
=======
Contributors
------------
* Raphaël Reverdy <raphael.reverdy@akretion.com>
Maintainer
----------
Akretion

View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion (https://akretion.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Purchase Order Buyer",
"summary": "Add a buyer (user) on POs",
"version": "10.0.1.1.0",
"author": "Akretion",
"website": "https://github.com/akretion/odoo-usability",
"category": "Purchases",
"depends": ["purchase"],
"data": [
'views/purchase_order.xml',
],
"license": "AGPL-3",
"installable": True,
"application": False,
}

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import purchase_order

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Raphael Reverdy https://akretion.com
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
user_id = fields.Many2one(
'res.users',
string='Buyer', index=True,
track_visibility='onchange',
default=lambda self: self.env.user)
@api.multi
@api.onchange('partner_id')
def onchange_partner_id(self):
"""Update the user_id (buyer)"""
for rec in self:
if rec.partner_id and rec.partner_id.user_id:
user_id = rec.partner_id.user_id.id
else:
user_id = self.env.user
return rec.update({
'user_id': user_id,
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="purchase_order_tree" model="ir.ui.view">
<field name="name">purchase.order.tree</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_tree"/>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="user_id" />
</field>
</field>
</record>
<record id="purchase_order_form" model="ir.ui.view">
<field name="name">purchase.order.form</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="user_id" />
</field>
</field>
</record>
</odoo>

View File

@@ -4,6 +4,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api
from odoo.tools.misc import formatLang
class PurchaseOrder(models.Model):
@@ -54,6 +55,20 @@ class PurchaseOrder(models.Model):
self, 'purchase.report_purchaseorder')
return action
# Re-write native name_get() to use amount_untaxed instead of amount_total
@api.multi
@api.depends('name', 'partner_ref')
def name_get(self):
result = []
for po in self:
name = po.name
if po.partner_ref:
name += ' ('+po.partner_ref+')'
if po.amount_untaxed:
name += ': ' + formatLang(self.env, po.amount_untaxed, currency_obj=po.currency_id)
result.append((po.id, name))
return result
class StockPicking(models.Model):
_inherit = 'stock.picking'

View File

@@ -23,7 +23,8 @@ class CrmLeadLost(models.TransientModel):
])
if quotes:
quotes.action_cancel()
quotes.message_post(_(
"Quotation automatically cancelled upon marking "
"the related opportunity as lost."))
for quote in quotes:
quote.message_post(_(
"Quotation automatically cancelled upon marking "
"the related opportunity as lost."))
return super(CrmLeadLost, self).action_lost_reason_apply()

View File

@@ -1,3 +1,4 @@
# -*- encoding: utf-8 -*-
from . import sale
from . import sale_report

View File

@@ -33,7 +33,7 @@ class SaleOrderLine(models.Model):
string='Margin in Company Currency', readonly=True, store=True,
compute='_compute_margin', currency_field='company_currency_id')
margin_rate = fields.Float(
string="Margin (%)", readonly=True, store=True,
string="Margin Rate", readonly=True, store=True,
compute='_compute_margin',
digits=(16, 2), help="Margin rate in percentage of the sale price")
@@ -77,7 +77,7 @@ class SaleOrderLine(models.Model):
sale_uom = self.env['product.uom'].browse(sale_uom_id)
# convert from product UoM to sale UoM
std_price = pp.uom_id._compute_price(
standard_price, sale_uom)
pp.standard_price, sale_uom)
vals['standard_price_company_currency'] = std_price
return super(SaleOrderLine, self).create(vals)

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class SaleReport(models.Model):
_inherit = 'sale.report'
margin = fields.Float(string='Margin', readonly=True)
def _select(self):
select_str = super(SaleReport, self)._select()
select_str += ", SUM(l.margin_company_currency) AS margin"
return select_str

View File

@@ -20,13 +20,17 @@
groups="account.group_account_user"/>
<field name="company_currency_id" invisible="1"/>
</field>
<xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="after">
<field name="standard_price_sale_currency" groups="base.group_no_one" string="Cost Price Sale Cur."/>
<field name="standard_price_company_currency" groups="base.group_no_one" string="Cost Price Comp. Cur."/>
<field name="margin_sale_currency" groups="base.group_no_one" string="Margin Sale Cur."/>
<field name="margin_company_currency" groups="base.group_no_one" string="Margin Comp. Cur."/>
<field name="margin_rate" groups="base.group_no_one"/>
<xpath expr="//field[@name='order_line']/form//field[@name='analytic_tag_ids']/.." position="after">
<field name="standard_price_sale_currency" groups="base.group_no_one"/>
<field name="standard_price_company_currency" groups="base.group_no_one"/>
<field name="margin_sale_currency" groups="base.group_no_one"/>
<field name="margin_company_currency" groups="base.group_no_one"/>
<label for="margin_rate"/>
<div name="margin_rate">
<field name="margin_rate" groups="base.group_no_one" class="oe_inline"/> %
</div>
<field name="company_currency_id" invisible="1"/>
<field name="currency_id" invisible="1"/>
</xpath>
</field>
</record>

View File

@@ -1,41 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Sale Margin Report module for Odoo
# Copyright (C) 2016 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Sale Margin Report',
'version': '0.1',
'category': 'Sales Management',
'license': 'AGPL-3',
'summary': 'Add margin measure in Sales Analysis',
'description': """
This module adds the measure *Margin* in the Sales Analysis pivot table. It is in a separate module because it depends on the module *bi_sale_company_currency* (in which I re-wrote the Sales Analysis pivot table).
This module has been written by Alexis de Lattre from Akretion
<alexis.delattre@akretion.com>.
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['sale_margin_no_onchange', 'bi_sale_company_currency'],
'data': [],
'installable': False,
}

View File

@@ -1,3 +0,0 @@
# -*- coding: utf-8 -*-
from . import sale_report

View File

@@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Sale Margin Report module for Odoo
# Copyright (C) 2016 Akretion (http://www.akretion.com/)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, fields
import openerp.addons.decimal_precision as dp
class SaleReportBi(models.Model):
_inherit = "sale.report.bi"
margin_company_currency = fields.Float(
string='Margin', readonly=True,
digits=dp.get_precision('Account'))
def _select(self):
select = super(SaleReportBi, self)._select()
select += """
, sum(sol.margin_company_currency) AS margin_company_currency
"""
return select

View File

@@ -15,7 +15,9 @@
<xpath expr="//field[@name='order_line']/form//label[@for='analytic_tag_ids']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/form//field[@name='analytic_tag_ids']/.." position="replace"/>
<xpath expr="//field[@name='order_line']/form//field[@name='analytic_tag_ids']/.." position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='analytic_tag_ids']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>

View File

@@ -9,4 +9,4 @@ class MrpBom(models.Model):
_inherit = 'mrp.bom'
_rec_name = 'product_id'
sale_ok = fields.Boolean(related='product_id.sale_ok', store=True)
sale_ok = fields.Boolean(related='product_id.sale_ok', store=True, compute_sudo=True)

View File

@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
from . import report
from . import sale

View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion France (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Sale Order Full Dropship',
'version': '10.0.1.0.0',
'category': 'Sales',
'license': 'AGPL-3',
'summary': 'Adds an option full dropship on sale orders',
'description': """
Sale Order Full Dropship
========================
This module adds a boolean field *Full Dropship* on sale order form. If enabled on a quotation, Odoo will enable the *Dropship* route on all sale order lines of that order. That way, for a full dropship order, the user won't have to open each order line to set the Dropship route.
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': [
'sale_stock',
'stock_dropshipping',
],
'data': [
'sale_view.xml',
],
'installable': True,
}

View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion France (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models, _
from odoo.exceptions import UserError
class SaleOrder(models.Model):
_inherit = 'sale.order'
dropship = fields.Boolean(
string='Dropship', readonly=True,
states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
help="If enabled, all the order lines will be set with the "
"'Drop Shipping' route upon confirmation of the order.")
def action_confirm(self):
try:
ds_route = self.env.ref('stock_dropshipping.route_drop_shipping')
except ValueError:
raise UserError(_("Drop shipping route not found."))
for order in self:
if order.dropship:
# no need to exclude service lines
# by default, the don't generate a procurement
order.order_line.write({'route_id': ds_route.id})
return super(SaleOrder, self).action_confirm()

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Akretion (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_order_form_inherit_sale_stock" model="ir.ui.view">
<field name="name">sale_order_full_dropship.sale.order.form</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale_stock.view_order_form_inherit_sale_stock"/>
<field name="arch" type="xml">
<field name="partner_shipping_id" position="after">
<field name="dropship"/>
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,5 @@
# coding: utf-8
# © 2017 Chafique DELLI @ Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import models

View File

@@ -0,0 +1,21 @@
# coding: utf-8
# © 2017 Chafique DELLI @ Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'Partner Shipping Filter with Customer',
'summary': "Shows only delivery addresses that are linked "
"with the customer",
'version': '10.0.1.0.0',
'category': 'Sale Management',
'website': 'http://akretion.com',
'author': 'Akretion, Odoo Community Association (OCA)',
'license': 'AGPL-3',
'installable': True,
'depends': [
'sale',
],
'data': [
'views/sale_view.xml',
]
}

View File

@@ -0,0 +1,5 @@
# coding: utf-8
# © 2017 Chafique DELLI @ Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import sale

View File

@@ -0,0 +1,12 @@
# coding: utf-8
# © 2017 Chafique DELLI @ Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models, fields
class SaleOrder(models.Model):
_inherit = 'sale.order'
commercial_partner_id = fields.Many2one(
related='partner_id.commercial_partner_id', readonly=True)

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_order_form" model="ir.ui.view">
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='partner_id']" position="after">
<field name="commercial_partner_id" invisible="1"/>
</xpath>
<xpath expr="//field[@name='partner_shipping_id']" position="attributes">
<attribute name="domain">['|',
('id', '=', partner_id), '&amp;',
('type','=', 'delivery'), '&amp;',
('id', 'child_of', commercial_partner_id), ('parent_id', '!=', False)]</attribute>
</xpath>
</field>
</record>
</odoo>

View File

@@ -26,6 +26,7 @@ This module has been written by Alexis de Lattre from Akretion
'depends': ['sale'],
'data': [
'sale_view.xml',
'sale_report_view.xml',
'product_view.xml',
'security/ir.model.access.csv',
],

View File

@@ -4,6 +4,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models
from collections import OrderedDict
class AccountInvoice(models.Model):
@@ -15,7 +16,7 @@ class AccountInvoice(models.Model):
# https://github.com/akretion/odoo-py3o-report-templates/tree/10.0/account_invoice_report_py3o
def py3o_lines_layout(self):
self.ensure_one()
res1 = {}
res1 = OrderedDict()
# {'categ(6)': {'lines': [l1, l2], 'subtotal': 23.32}}
for line in self.invoice_line_ids:
categ = line.layout_category_id
@@ -52,7 +53,7 @@ class AccountInvoice(models.Model):
# defined above: you just have to change the call in the invoice
# ODT template
self.ensure_one()
res1 = {}
res1 = OrderedDict()
# {categ(1): {'lines': [l1, l2], 'subtotal': 23.32}}
soo = self.env['sale.order']
for line in self.invoice_line_ids:

View File

@@ -4,6 +4,7 @@
from odoo import models, fields, api
from odoo.tools import float_is_zero
from collections import OrderedDict
class SaleOrder(models.Model):
@@ -55,7 +56,7 @@ class SaleOrder(models.Model):
@api.multi
def py3o_lines_layout(self):
self.ensure_one()
res1 = {}
res1 = OrderedDict()
# {categ(6): {'lines': [l1, l2], 'subtotal': 23.32}}
for line in self.order_line:
categ = line.layout_category_id

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Akretion (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="sale_report_tree" model="ir.ui.view">
<field name="name">usability.sale.report.tree</field>
<field name="model">sale.report</field>
<field name="arch" type="xml">
<tree string="Sales Analysis">
<field name="name"/>
<field name="date"/>
<field name="commercial_partner_id"/>
<field name="user_id"/>
<field name="product_id"/>
<field name="product_uom_qty" sum="1"/>
<field name="qty_delivered" sum="1"/>
<field name="qty_to_invoice" sum="1"/>
<field name="product_uom" groups="product.group_uom"/>
<field name="price_subtotal" sum="1"/>
<field name="state"/>
</tree>
</field>
</record>
<record id="sale.action_order_report_all" model="ir.actions.act_window">
<field name="context">{'search_default_Sales': 1}</field> <!-- Remove group_by_no_leaf, which breaks tree view -->
</record>
<record id="view_order_product_pivot" model="ir.ui.view">
<field name="name">usability.sale.report.pivot</field>
<field name="model">sale.report</field>
<field name="inherit_id" ref="sale.view_order_product_pivot"/>
<field name="arch" type="xml">
<pivot position="attributes">
<attribute name="disable_linking"></attribute>
</pivot>
</field>
</record>
</odoo>

View File

@@ -32,6 +32,13 @@
<button name="action_cancel" type="object" position="attributes">
<attribute name="confirm">Are you sure you want to cancel this picking?</attribute>
</button>
<!-- This sum is useful to check the 'number of items' to transfer... -->
<xpath expr="//field[@name='pack_operation_product_ids']/tree/field[@name='product_qty']" position="attributes">
<attribute name="sum">1</attribute>
</xpath>
<xpath expr="//field[@name='pack_operation_product_ids']/tree/field[@name='qty_done']" position="attributes">
<attribute name="sum">1</attribute>
</xpath>
</field>
</record>
@@ -216,6 +223,20 @@
</field>
</record>
<record id="view_pack_operation_lot_form" model="ir.ui.view">
<field name="name">stock_usability.stock.pack.operation.form</field>
<field name="model">stock.pack.operation</field>
<field name="inherit_id" ref="stock.view_pack_operation_lot_form" />
<field name="arch" type="xml">
<field name="product_id" position="after">
<field name="picking_source_location_id" invisible="1"/>
<field name="picking_destination_location_id" invisible="1"/>
<field name="location_id" domain="[('id', 'child_of', picking_source_location_id)]" groups="stock.group_stock_multi_locations"/>
<field name="location_dest_id" domain="[('id', 'child_of', picking_destination_location_id)]" groups="stock.group_stock_multi_locations"/>
</field>
</field>
</record>
<record id="view_warehouse" model="ir.ui.view">
<field name="name">stock.usability.warehouse.form</field>
<field name="model">stock.warehouse</field>
@@ -293,6 +314,10 @@ should be able to access it. So I add a menu entry under Inventory Control. -->
<attribute name="decoration-info">product_qty &gt; theoretical_qty</attribute>
<attribute name="decoration-warning">product_qty &lt; theoretical_qty</attribute>
</xpath>
<button name="reset_real_qty" type="object" position="attributes">
<attribute name="confirm">Are you sure you want to reset all quantities to 0 ?</attribute>
</button>
</field>
</record>

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import mrp

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Default Warehouse on User (MRP)',
'version': '10.0.1.0.0',
'category': 'Manufacturing',
'license': 'AGPL-3',
'summary': "Use the users's default warehouse on manufacturing orders",
'description': """
Default Warehouse on User (MRP)
================================
The default warehouse configured in the preferences of the user will be used by default on manufacturing orders.
This module has been written by Alexis de Lattre from Akretion
<alexis.delattre@akretion.com>.
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['mrp', 'stock_user_default_warehouse_base'],
'installable': True,
}

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api
class MrpProduction(models.Model):
_inherit = 'mrp.production'
@api.model
def _default_pref_picking_type(self):
default_manu_type = self.env.user.context_default_warehouse_id.\
manu_type_id
if default_manu_type:
return default_manu_type.id
return self._get_default_picking_type()
# No need to inherit the default value of location_src_id and
# location_dest_id because it is immediately over-ridden
# by the onchange of picking_type_id
picking_type_id = fields.Many2one(default=_default_pref_picking_type)

View File

@@ -14,6 +14,6 @@ class PurchaseOrder(models.Model):
default_in_type = self.env.user.context_default_warehouse_id.in_type_id
if default_in_type:
return default_in_type.id
return self._default_picking_type
return self._default_picking_type()
picking_type_id = fields.Many2one(default=_default_pref_picking_type)