Compare commits

...

79 Commits

Author SHA1 Message Date
Sébastien BEAU
51fa6f018e [REF] refactor code, attribute improvement view should be all in attribute_view instead of 2 modules 2017-10-04 15:59:06 +02:00
Alexis de Lattre
3888495071 Add field has_attachment on invoices 2017-09-29 14:36:57 +02:00
chafique delli
93acac8e19 Merge pull request #43 from akretion/8.0-mail-usability
add many2many_tags_email.js file for mail_usability that replaces addons-mail-fix.diff patch
2017-09-26 15:51:56 +02:00
Alexis de Lattre
44b32672e2 2 fixes in hr_holidays modules 2017-09-22 15:57:16 +02:00
Alexis de Lattre
1160cce1d8 Allow to duplicate an account move that belongs to a closed period => copy=False on period_id and date of account.move 2017-09-20 12:17:45 +02:00
chafique-delli
16664bcb7f add comment in many2many_tags_email.js file 2017-09-18 16:15:18 +02:00
chafique-delli
cfb92ec23a add many2many_tags_email.js file for mail_usability that replaces addons-mail-fix.diff patch 2017-09-18 15:44:58 +02:00
Sébastien BEAU
a3844004fb [FIX] fix showing all variant active or inactive 2017-09-16 12:28:22 +02:00
Alexis de Lattre
9319b120bf Add patch for odoo when using mail_usability 2017-09-14 17:46:05 +02:00
Alexis de Lattre
7fd5ab9cf1 Filter on period instead of dates for aged open invoices report 2017-09-12 10:35:39 +02:00
Alexis de Lattre
8f463db179 translation=False on 'name' of res.partner.category and crm.case.categ 2017-09-07 18:19:24 +02:00
Sébastien BEAU
f8720c7a36 [FIX] fix broken menu due to mistyping for context in reception menu 2017-08-17 10:27:30 +02:00
David Beal
e74e53c804 FIX make defaut module filter to installable 2017-08-16 12:43:41 +02:00
Sébastien BEAU
5388638941 [FIX] fix filtering partner on name and ref and not on email 2017-08-10 17:13:31 +02:00
mourad-ehm
d943ef262f Merge pull request #41 from akretion/fix_change_button_name_bug
[FIX] change button name bug
2017-08-04 14:49:13 +02:00
Sébastien BEAU
a787e092ed [IMP] add delivery usability 2017-08-03 20:30:44 +02:00
Sébastien BEAU
8e5f0b0afa [IMP] add type to be easier to customise button in custom module 2017-08-03 20:27:39 +02:00
Sébastien BEAU
cb21028a48 [IMP] add stock split menu 2017-08-03 14:32:53 +02:00
Sébastien BEAU
ffa6e74a19 [IMP] order invoice per date, solve issue with multi journal 2017-08-02 19:39:33 +02:00
Alexis de Lattre
1e062af883 Add wizard to mark all invoices as sent and add filter on invoices to send 2017-07-25 18:12:05 +02:00
Alexis de Lattre
31f191aecb Disable the constraint _check_account_type() on account.account, as agreed with Sebastien 2017-07-25 11:38:03 +02:00
Alexis de Lattre
51c982bf2f Fiscal value cols now have to right sign, to use with the updated version of l10n_fr_intrastat_product 2017-07-24 14:02:07 +02:00
Mourad Elhadj Mimoune
3d7241407e [FIX] Button name change bug 2017-07-11 15:32:07 +02:00
Alexis de Lattre
817fadd167 Add groupby on commercial partner on invoice line search 2017-06-30 17:19:39 +02:00
Alexis de Lattre
25ea1991cf Add module account_invoice_update_wizard 2017-06-21 23:57:29 +02:00
Alexis de Lattre
c8c1f78e66 FIX my previous commit in mail_usability 2017-06-21 15:34:35 +02:00
Alexis de Lattre
0bc6cb32d9 Merge branch '8.0' of github.com:akretion/odoo-usability into 8.0 2017-06-21 14:53:13 +02:00
Alexis de Lattre
db736d95a3 Add "All Messages Except Notifications" for notify_email on res.partner 2017-06-21 14:52:47 +02:00
Alexis de Lattre
f83c948889 Add check methods for accounting and improve search view of account.invoice.report 2017-06-14 17:31:45 +02:00
Alexis de Lattre
d736663f3f Fix view 2017-05-19 09:47:10 +02:00
David Beal
f051537a92 FIX import report_xls in account_move_line_start_end_dates_xls 2017-05-15 12:01:49 +02:00
Alexis de Lattre
193e5e6ecc Add multi-company support hr_holidays_usability
Add modules hr_holidays_lunch_voucher and hr_holidays_lunch_voucher_natixis
2017-05-11 00:29:22 +02:00
Alexis de Lattre
00592100f7 Add module resource_usability 2017-05-09 17:09:34 +02:00
Alexis de Lattre
0d524f27d8 Merge branch '8.0' of github.com:akretion/odoo-usability into 8.0 2017-05-09 12:10:18 +02:00
Alexis de Lattre
4509b2631a Add multi-company record rule on leads
Small view improvement in crm_usability
2017-05-09 12:09:39 +02:00
Alexis de Lattre
2ff7943c62 Account_usability now compatible with account_analytic_plans 2017-05-04 17:13:48 +02:00
beau sebastien
d85e7270b3 Merge pull request #28 from akretion/sale-partner-shipping-filter-with-customer
[8.0] sale_partner_shipping_filter_with_customer module
2017-04-21 13:51:47 +02:00
beau sebastien
7b6916ce16 Merge pull request #29 from akretion/8.0-product-unit-manager-group
[8.0] product_unit_manager_group module
2017-04-21 11:27:26 +02:00
chafique-delli
a9c6f9ed69 replace partner_parent_id by commercial_partner_id 2017-04-13 21:50:12 +02:00
Alexis de Lattre
7a11966992 Better views for product.ul
Track partner_id on stock.picking
Improve stock.quant.package view
2017-04-13 20:05:15 +02:00
Alexis de Lattre
d4fcaa7d14 account.move.line: Add filter on both debit and credit 2017-03-23 14:38:15 +01:00
chafique-delli
77fb97f273 The units of measurement menus are visible only to the group_uom_manager group 2017-03-17 17:07:29 +01:00
Sébastien BEAU
9670eb7c10 [IMP] improve attribute view 2017-03-16 15:33:45 +01:00
chafique-delli
f8e955d5b7 remove dependency to point_of_sale to be more generic 2017-03-14 12:37:13 +01:00
chafique-delli
a803d1dcb9 add a dependency to the mrp and point_of_sale module 2017-03-14 12:14:44 +01:00
chafique-delli
a141f0506b add product_unit_manager_group module 2017-03-14 11:20:47 +01:00
chafique-delli
7c554aee33 fix after @bealdav's comment 2017-03-09 18:02:04 +01:00
chafique-delli
07c81e441d domain improvement 2017-03-09 15:14:05 +01:00
Alexis de Lattre
4c3c8e50db Remove show_account_move_form because of the fact that the context is kept from the account.move.line form view, so it triggers a crash when creating a line from the account.move form
Fix domain on analytic account on move lines
2017-03-08 00:31:23 +01:00
chafique-delli
60beb99580 fix domain 2017-03-07 19:13:30 +01:00
chafique-delli
709bde8fb7 add sale_partner_shipping_filter_with_customer module 2017-03-07 18:42:07 +01:00
Alexis de Lattre
281c0b7052 Cut ref field to 32 chars, to avoid trouble with PDF webkit accounting reports 2017-03-04 22:34:31 +01:00
Alexis de Lattre
8d55f9aaa5 Restore description field 2017-03-04 22:34:18 +01:00
Alexis de Lattre
964b1fca67 Better context from wizard 2017-03-03 09:36:44 +01:00
Alexis de Lattre
e20f88baf0 Add button for direct access to journal entry from journal items 2017-03-03 09:36:08 +01:00
Benoit
15d9c31f6f [FIX] change pattern for mail footer because it changes is there is user signature or not 2017-02-15 11:37:29 +01:00
Alexis de Lattre
e74864e54f Add FR translation 2017-02-14 14:25:41 +01:00
Alexis de Lattre
146c0e6b7d Add a link from incoming picking to PO 2017-02-10 17:27:27 +01:00
Alexis de Lattre
869c7176f7 Supplierinfo: remove field that is not usefull for everybody ; fix addition of supplier on products 2017-02-09 22:23:36 +01:00
Alexis de Lattre
4b26ab78be Better views on product.supplierinfo 2017-02-09 16:51:00 +01:00
Alexis de Lattre
662494db35 Better selection of picking type 2017-02-09 14:09:41 +01:00
Alexis de Lattre
aafc36dfdb Fix XMLID name 2017-02-07 17:37:01 +01:00
Alexis de Lattre
e94168ad27 Better procurement order search view 2017-02-07 16:48:24 +01:00
Sébastien BEAU
9b62198115 [FIX] fix missing dependency for partner_split_menu 2017-02-06 19:21:36 +01:00
Alexis de Lattre
309027198a Add module product_expiry_simple 2017-02-03 23:24:08 +01:00
Alexis de Lattre
c8f1517881 Add traking on some import product fields 2017-02-03 15:13:40 +01:00
Alexis de Lattre
6cb195a3eb Remove stored field product_supplier_code on stock.move because it takes a very very long time to compute on existing large DB and it is not needed by everybody. (#27) 2017-02-03 12:06:09 +01:00
Alexis de Lattre
b85f5184c2 Add track_visibility on important fields 2017-02-02 09:40:26 +01:00
Sébastien BEAU
2d392ce465 [FIX] fix product view with variant 2017-01-31 15:41:40 +01:00
Sébastien BEAU
e71ad5f952 [IMP] add module partner split menu to avoid the mix of contact and main partner 2017-01-26 14:40:47 +01:00
Alexis de Lattre
3905a826fa Small improvements on contracts view 2017-01-20 19:54:22 +01:00
Alexis de Lattre
18ea374d8c Add module account_analytic_analysis_usability 2017-01-20 19:27:55 +01:00
Alexis de Lattre
f8d3340e48 Better name 2017-01-20 18:43:12 +01:00
Alexis de Lattre
9761c7527b Improvement on invoice lines and analytic accounts 2017-01-20 18:22:37 +01:00
Alexis de Lattre
baf7359cc7 Show state column in all crm.phonecall tree view 2017-01-19 18:43:40 +01:00
Sébastien BEAU
33561ed930 [FIX] create variant manually is not allowed 2017-01-16 11:52:07 +01:00
Sébastien BEAU
aad2121f20 [IMP] add module product_variant_usability 2017-01-10 15:29:37 +01:00
Sébastien BEAU
880cf7c7a9 [ADD] add module product_simple_accounting to simplify the product form regarding accounting 2017-01-09 19:35:46 +01:00
Alexis de Lattre
d3eef1e5a5 Add start_date and end_date on bank statements 2016-12-21 21:13:40 +01:00
122 changed files with 3161 additions and 107 deletions

View File

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

View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# © 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Account Analytic Analysis Usability',
'version': '8.0.1.0.0',
'category': 'Accounting & Finance',
'license': 'AGPL-3',
'summary': 'Usability improvements on Account Analytic Analysis',
'description': """
Account Analytic Analysis Usability
===================================
Usability improvements include:
* add next invoice date in tree view of contrats (and add a group by)
This module has been written by Alexis de Lattre from Akretion
<alexis.delattre@akretion.com>.
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['account_analytic_analysis'],
'data': [
'analytic_view.xml',
],
'installable': True,
}

View File

@@ -0,0 +1,14 @@
# -*- 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 openerp import models, fields
class AccountAnalyticAccount(models.Model):
_inherit = "account.analytic.account"
recurring_next_date = fields.Date(track_visibility='onchange')
recurring_rule_type = fields.Selection(track_visibility='onchange')
recurring_interval = fields.Integer(track_visibility='onchange')
recurring_invoices = fields.Boolean(track_visibility='onchange')

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<openerp>
<data>
<record id="view_account_analytic_account_tree_c2c_3" model="ir.ui.view">
<field name="name">usability.analytic_analysis.account.analytic.account.tree.contract</field>
<field name="model">account.analytic.account</field>
<field name="inherit_id" ref="account_analytic_analysis.view_account_analytic_account_tree_c2c_3"/>
<field name="groups_id" eval="[(4, ref('account.group_account_invoice'))]"/>
<field name="arch" type="xml">
<field name="date" position="after">
<field name="recurring_rule_type"/>
<field name="recurring_next_date"/>
</field>
</field>
</record>
<record id="view_account_analytic_account_overdue_search" model="ir.ui.view">
<field name="name">usability.analytic_analysis.account.analytic.account.search</field>
<field name="model">account.analytic.account</field>
<field name="inherit_id" ref="account_analytic_analysis.view_account_analytic_account_overdue_search"/>
<field name="arch" type="xml">
<filter context="{'group_by' : 'pricelist_id'}" position="after">
<filter name="recurring_rule_type_groupby" string="Invoicing Frequency" context="{'group_by': 'recurring_rule_type'}"/>
<filter name="recurring_next_date_groupby" string="Next Invoice Date" context="{'group_by': 'recurring_next_date'}"/>
</filter>
<filter name="cancelled" position="after">
<separator/>
<filter name="recurring_invoices" string="Recurring Invoicing" domain="[('recurring_invoices', '=', True)]"/>
</filter>
</field>
</record>
</data>
</openerp>

View File

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

View File

@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# © 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Account Invoice Update Wizard',
'version': '8.0.1.0.0',
'category': 'Accounting & Finance',
'license': 'AGPL-3',
'summary': 'Wizard to update non-legal fields of an open/paid invoice',
'description': """
Account Invoice Update Wizard
=============================
This module adds a button *Update Invoice* on Customer and Supplier invoices in Open or Paid state. This button starts a wizard which allows the user to update non-legal fields of the invoice:
* Source Document
* Reference/Description
* Payment terms (update allowed only to a payment term with same number of terms of the same amount and on invoices without any payment)
* Bank Account
* Salesman
* Notes
* Description of invoice lines
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['account'],
'data': [
'wizard/account_invoice_update_view.xml',
'views/account_invoice.xml',
],
'installable': True,
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<openerp>
<data>
<record id="invoice_supplier_form" model="ir.ui.view">
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_supplier_form"/>
<field name="arch" type="xml">
<button name="invoice_cancel" position="before">
<button name="%(account_invoice_update_action)d" type="action" string="Update Invoice" states="open,paid" groups="account.group_account_invoice"/>
</button>
</field>
</record>
<record id="invoice_form" model="ir.ui.view">
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form"/>
<field name="arch" type="xml">
<button name="invoice_cancel" position="before">
<button name="%(account_invoice_update_action)d" type="action" string="Update Invoice" states="open,paid" groups="account.group_account_invoice"/>
</button>
</field>
</record>
</data>
</openerp>

View File

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

View File

@@ -0,0 +1,206 @@
# -*- 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 openerp import models, fields, api, _
from openerp.exceptions import Warning as UserError
import openerp.addons.decimal_precision as dp
class AccountInvoiceUpdate(models.TransientModel):
_name = 'account.invoice.update'
_description = 'Wizard to update non-legal fields of invoice'
invoice_id = fields.Many2one(
'account.invoice', string='Invoice', required=True,
readonly=True)
type = fields.Selection(related='invoice_id.type', readonly=True)
company_id = fields.Many2one(
related='invoice_id.company_id', readonly=True)
commercial_partner_id = fields.Many2one(
related='invoice_id.commercial_partner_id', readonly=True)
user_id = fields.Many2one('res.users', string='Salesperson')
# I use the same field name as the original invoice field name
# even if it the name is "bad"
# Updating payment_term will not work if you use
# the OCA module account_constraints (you will just get an error)
payment_term = fields.Many2one(
'account.payment.term', string='Payment Terms')
reference = fields.Char(string='Invoice Reference')
name = fields.Char(string='Reference/Description')
origin = fields.Char(string='Source Document')
comment = fields.Text('Additional Information')
partner_bank_id = fields.Many2one(
'res.partner.bank', string='Bank Account')
line_ids = fields.One2many(
'account.invoice.line.update', 'parent_id', string='Invoice Lines')
@api.model
def _simple_fields2update(self):
'''List boolean, date, datetime, char, text fields'''
return ['reference', 'name', 'origin', 'comment']
@api.model
def _m2o_fields2update(self):
return ['payment_term', 'user_id', 'partner_bank_id']
@api.model
def _prepare_default_get(self, invoice):
res = {'invoice_id': invoice.id, 'line_ids': []}
for sfield in self._simple_fields2update():
res[sfield] = invoice[sfield]
for m2ofield in self._m2o_fields2update():
res[m2ofield] = invoice[m2ofield].id or False
for line in invoice.invoice_line:
res['line_ids'].append({
'invoice_line_id': line.id,
'name': line.name,
'quantity': line.quantity,
'price_subtotal': line.price_subtotal,
})
return res
@api.model
def default_get(self, fields_list):
res = super(AccountInvoiceUpdate, self).default_get(fields_list)
assert self._context.get('active_model') == 'account.invoice',\
'active_model should be account.invoice'
inv = self.env['account.invoice'].browse(self._context['active_id'])
res = self._prepare_default_get(inv)
return res
@api.onchange('type')
def type_on_change(self):
res = {'domain': {}}
if self.type in ('out_invoice', 'out_refund'):
res['domain']['partner_bank_id'] =\
"[('partner_id.ref_companies', 'in', [company_id])]"
else:
res['domain']['partner_bank_id'] =\
"[('partner_id', '=', commercial_partner_id)]"
return res
@api.multi
def _prepare_invoice(self):
vals = {}
inv = self.invoice_id
for sfield in self._simple_fields2update():
if self[sfield] != inv[sfield]:
vals[sfield] = self[sfield]
for m2ofield in self._m2o_fields2update():
if self[m2ofield] != inv[m2ofield]:
vals[m2ofield] = self[m2ofield].id or False
if 'payment_term' in vals:
pterm_list = self.payment_term.compute(
value=1, date_ref=inv.date_invoice)[0]
if pterm_list:
vals['date_due'] = max(line[0] for line in pterm_list)
return vals
@api.model
def _prepare_invoice_line(self, line):
vals = {}
if line.name != line.invoice_line_id.name:
vals['name'] = line.name
return vals
@api.multi
def _prepare_move(self):
mvals = {}
inv = self.invoice_id
ini_ref = inv.move_id.ref
ref = inv.reference or inv.name
if ini_ref != ref:
mvals['ref'] = ref
return mvals
@api.multi
def _update_payment_term_move(self):
self.ensure_one()
inv = self.invoice_id
if (
self.payment_term and
self.payment_term != inv.payment_term and
inv.move_id and
inv.move_id.period_id.state == 'draft'):
# I don't update pay term when the invoice is partially (or fully)
# paid because if you have a payment term with several lines
# of the same amount, you would also have to take into account
# the reconcile marks to put the new maturity date on the right
# lines
if inv.payment_ids:
raise UserError(_(
"This wizard doesn't support the update of payment "
"terms on an invoice which is partially or fully "
"paid."))
prec = self.env['decimal.precision'].precision_get('Account')
term_res = self.payment_term.compute(
inv.amount_total, inv.date_invoice)[0]
new_pterm = {} # key = int(amount * 100), value = [date1, date2]
for entry in term_res:
amount = int(entry[1] * 10 * prec)
if amount in new_pterm:
new_pterm[amount].append(entry[0])
else:
new_pterm[amount] = [entry[0]]
mlines = {} # key = int(amount * 100), value : [line1, line2]
for line in inv.move_id.line_id:
if line.account_id == inv.account_id:
amount = int(abs(line.credit - line.debit) * 10 * prec)
if amount in mlines:
mlines[amount].append(line)
else:
mlines[amount] = [line]
for iamount, lines in mlines.iteritems():
if len(lines) != len(new_pterm.get(iamount, [])):
raise UserError(_(
"The original payment term '%s' doesn't have the "
"same terms (number of terms and/or amount) as the "
"new payment term '%s'. You can only switch to a "
"payment term that has the same number of terms "
"with the same amount.") % (
inv.payment_term.name, self.payment_term.name))
for line in lines:
line.date_maturity = new_pterm[iamount].pop()
@api.multi
def run(self):
self.ensure_one()
inv = self.invoice_id
updated = False
# re-write date_maturity on move line
self._update_payment_term_move()
ivals = self._prepare_invoice()
if ivals:
updated = True
inv.write(ivals)
for line in self.line_ids:
ilvals = self._prepare_invoice_line(line)
if ilvals:
updated = True
line.invoice_line_id.write(ilvals)
if inv.move_id and inv.move_id.period_id.state == 'draft':
mvals = self._prepare_move()
if mvals:
inv.move_id.write(mvals)
if updated:
inv.message_post(_(
'Non-legal fields of invoice updated via the Invoice Update '
'wizard.'))
return True
class AccountInvoiceLineUpdate(models.TransientModel):
_name = 'account.invoice.line.update'
_description = 'Update non-legal fields of invoice lines'
parent_id = fields.Many2one(
'account.invoice.update', string='Wizard', ondelete='cascade')
invoice_line_id = fields.Many2one(
'account.invoice.line', string='Invoice Line', readonly=True)
name = fields.Text(string='Description', required=True)
quantity = fields.Float(
string='Quantity', digits=dp.get_precision('Product Unit of Measure'),
readonly=True)
price_subtotal = fields.Float(
string='Amount', readonly=True, digits=dp.get_precision('Account'))

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<openerp>
<data>
<record id="account_invoice_update_form" model="ir.ui.view">
<field name="model">account.invoice.update</field>
<field name="arch" type="xml">
<form string="Update Invoice Wizard">
<group name="main">
<field name="invoice_id" invisible="1"/>
<field name="type" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="commercial_partner_id" invisible="1"/>
<field name="reference" attrs="{'invisible': [('type', 'not in', ('in_invoice', 'in_refund'))]}"/>
<field name="origin"/>
<field name="name"/>
<field name="payment_term" widget="selection"/>
<field name="partner_bank_id"/>
<field name="user_id"/>
<field name="comment"/>
</group>
<group name="lines">
<field name="line_ids" nolabel="1">
<tree editable="bottom">
<field name="invoice_line_id" invisible="1"/>
<field name="name"/>
<field name="quantity"/>
<field name="price_subtotal"/>
</tree>
</field>
</group>
<footer>
<button name="run" type="object" class="oe_highlight" string="Update"/>
or
<button special="cancel" string="Cancel" class="oe_link"/>
</footer>
</form>
</field>
</record>
<record id="account_invoice_update_action" model="ir.actions.act_window">
<field name="name">Invoice Update Wizard</field>
<field name="res_model">account.invoice.update</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@@ -53,7 +53,10 @@ class AccountMoveLineFilterWizard(models.TransientModel):
self.ensure_one() self.ensure_one()
action = self.env['ir.actions.act_window'].for_xml_id( action = self.env['ir.actions.act_window'].for_xml_id(
'account', 'action_account_moves_all_a') 'account', 'action_account_moves_all_a')
action['context'] = {'search_default_account_id': [self.account_id.id]} action['context'] = {
'search_default_account_id': [self.account_id.id],
'journal_show_code_only': True,
}
if self.partner_id: if self.partner_id:
action['context']['search_default_partner_id'] =\ action['context']['search_default_partner_id'] =\
[self.partner_id.id] [self.partner_id.id]

View File

@@ -21,9 +21,16 @@
############################################################################## ##############################################################################
import xlwt import xlwt
import logging
from openerp import models, api from openerp import models, api
from openerp.addons.report_xls.utils import _render
from openerp.addons.report_xls.report_xls import report_xls _logger = logging.getLogger(__name__)
try:
from openerp.addons.report_xls.utils import _render
from openerp.addons.report_xls.report_xls import report_xls
except (ImportError, IOError) as err:
_logger.debug(err)
class AccountMoveLine(models.Model): class AccountMoveLine(models.Model):

View File

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

View File

@@ -49,6 +49,9 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
'website': 'http://www.akretion.com', 'website': 'http://www.akretion.com',
'depends': ['account'], 'depends': ['account'],
'conflicts': ['account_invoice_overdue_filter'], 'conflicts': ['account_invoice_overdue_filter'],
'data': ['account_view.xml'], 'data': [
'account_view.xml',
'wizard/account_invoice_mark_sent_view.xml',
],
'installable': True, 'installable': True,
} }

View File

@@ -23,10 +23,13 @@
from openerp import models, fields, api, _ from openerp import models, fields, api, _
from openerp.tools import float_compare from openerp.tools import float_compare
from openerp.exceptions import Warning as UserError from openerp.exceptions import Warning as UserError
import logging
logger = logging.getLogger(__name__)
class AccountInvoice(models.Model): class AccountInvoice(models.Model):
_inherit = 'account.invoice' _inherit = 'account.invoice'
_order = "date_invoice desc, number desc, id desc"
origin = fields.Char(track_visibility='onchange') origin = fields.Char(track_visibility='onchange')
supplier_invoice_number = fields.Char(track_visibility='onchange') supplier_invoice_number = fields.Char(track_visibility='onchange')
@@ -41,9 +44,41 @@ class AccountInvoice(models.Model):
journal_id = fields.Many2one(track_visibility='onchange') journal_id = fields.Many2one(track_visibility='onchange')
partner_bank_id = fields.Many2one(track_visibility='onchange') partner_bank_id = fields.Many2one(track_visibility='onchange')
fiscal_position = fields.Many2one(track_visibility='onchange') fiscal_position = fields.Many2one(track_visibility='onchange')
# has_attachment is useful for those who use attachment to archive
# supplier invoices. It allows them to find supplier invoices
# that don't have any attachment
has_attachment = fields.Boolean(
compute='_compute_has_attachment',
search='_search_has_attachment', readonly=True)
@api.multi @api.multi
def onchange_payment_term_date_invoice(self, payment_term_id, date_invoice): def _compute_has_attachment(self):
iao = self.env['ir.attachment']
for inv in self:
if iao.search([
('res_model', '=', 'account.invoice'),
('res_id', '=', inv.id),
('type', '=', 'binary'),
('company_id', '=', inv.company_id.id)], limit=1):
inv.has_attachment = True
else:
inv.has_attachment = False
def _search_has_attachment(self, operator, value):
att_inv_ids = {}
if operator == '=':
search_res = self.env['ir.attachment'].search_read([
('res_model', '=', 'account.invoice'),
('type', '=', 'binary'),
('res_id', '!=', False)], ['res_id'])
for att in search_res:
att_inv_ids[att['res_id']] = True
res = [('id', value and 'in' or 'not in', att_inv_ids.keys())]
return res
@api.multi
def onchange_payment_term_date_invoice(
self, payment_term_id, date_invoice):
res = super(AccountInvoice, self).onchange_payment_term_date_invoice( res = super(AccountInvoice, self).onchange_payment_term_date_invoice(
payment_term_id, date_invoice) payment_term_id, date_invoice)
if res and isinstance(res, dict) and 'value' in res: if res and isinstance(res, dict) and 'value' in res:
@@ -54,8 +89,8 @@ class AccountInvoice(models.Model):
# generated from customer invoices linked to the partners' account because: # 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 # 1) the label of an account move line is an important field, we can't
# write a rubbish '/' in it ! # write a rubbish '/' in it !
# 2) the 'name' field of the account.move.line is used in the overdue letter, # 2) the 'name' field of the account.move.line is used in the overdue
# and '/' is not meaningful for our customer ! # letter and '/' is not meaningful for our customer !
@api.multi @api.multi
def action_number(self): def action_number(self):
res = super(AccountInvoice, self).action_number() res = super(AccountInvoice, self).action_number()
@@ -69,6 +104,28 @@ class AccountInvoice(models.Model):
return res return res
class AccountInvoiceLine(models.Model):
_inherit = 'account.invoice.line'
# In the 'account' module, we have related stored field for:
# company_id, partner_id
currency_id = fields.Many2one(
related='invoice_id.currency_id', readonly=True, store=True)
invoice_type = fields.Selection(
related='invoice_id.type', store=True, readonly=True)
date_invoice = fields.Date(
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)
state = fields.Selection(
related='invoice_id.state', store=True, readonly=True,
string='Invoice State')
invoice_number = fields.Char(
related='invoice_id.move_id.name', store=True, readonly=True,
string='Invoice Number')
class AccountFiscalYear(models.Model): class AccountFiscalYear(models.Model):
_inherit = 'account.fiscalyear' _inherit = 'account.fiscalyear'
@@ -105,10 +162,51 @@ class AccountAccount(models.Model):
else: else:
return super(AccountAccount, self).name_get() return super(AccountAccount, self).name_get()
def _check_account_type(self, cr, uid, ids, context=None):
'''Disable this native python constraint, because we want to be able
to configure payable/receivable accounts with an account type
with close_method == 'balance' in order to have opening entries with
fewer lines. It is not a problem because we always use
account_financial_report_webkit which doesn't take the detailed
opening entries into account.'''
return True
_constraints = [
# The method name must be exactly the same as the native
# method, in order to override it
(_check_account_type, 'No error message', ['user_type', 'type']),
]
@api.model
def check_account_hierarchy(self):
'''designed to be called by a script'''
accounts = self.env['account.account'].search([])
parent_accounts = self
for account in accounts:
if account.parent_id and account.parent_id not in parent_accounts:
parent_accounts += account.parent_id
err_msg = []
view_user_type = self.env.ref('account.data_account_type_view')
for pacc in parent_accounts:
if pacc.type != 'view':
err_msg.append(_(
'Parent account %s should have type=view '
'(current type=%s)') % (pacc.code, pacc.type))
if pacc.user_type != view_user_type:
err_msg.append(_(
'Parent account %s should have user_type=view (current '
'(user_type=%s)') % (pacc.code, pacc.user_type.name))
if err_msg:
raise UserError('\n'.join(err_msg))
class AccountAnalyticAccount(models.Model): class AccountAnalyticAccount(models.Model):
_inherit = 'account.analytic.account' _inherit = 'account.analytic.account'
invoice_line_ids = fields.One2many(
'account.invoice.line', 'account_analytic_id', 'Invoice Lines',
readonly=True)
@api.multi @api.multi
def name_get(self): def name_get(self):
if self._context.get('analytic_account_show_code_only'): if self._context.get('analytic_account_show_code_only'):
@@ -125,11 +223,55 @@ class AccountAnalyticAccount(models.Model):
class AccountMove(models.Model): class AccountMove(models.Model):
_inherit = 'account.move' _inherit = 'account.move'
# When ref is too long, the PDF general ledger report becomes
# unreadable
ref = fields.Char(size=32)
# If you want to migrate existing data :
# update account_move set ref=left(ref,32) where ref is not null;
# update account_move_line set ref=left(ref,32) where ref is not null;
# Allow to duplicate an account.move that belongs to a closed period
period_id = fields.Many2one(copy=False)
date = fields.Date(copy=False)
@api.onchange('date') @api.onchange('date')
def date_onchange(self): def date_onchange(self):
if self.date: if self.date:
self.period_id = self.env['account.period'].find(self.date) self.period_id = self.env['account.period'].find(self.date)
@api.model
def delete_move_no_lines(self):
'''designed to be called by a script'''
moves_no_lines = self.search([('line_id', '=', False)])
inv_moves_sr = self.env['account.invoice'].search_read(
[('move_id', '!=', False)], ['move_id'])
move2inv = {} # key=move_id, value=invoice_id
invoice_move_no_line = {} # key=ID, value=number
deleted_move_ids = []
for inv_move_sr in inv_moves_sr:
move2inv[inv_move_sr['move_id'][0]] = inv_move_sr['id']
for move in moves_no_lines:
for l in move.line_id:
raise UserError(_('Move %d has a line !') % move.id)
if move.id not in move2inv:
if move.state == 'posted':
move.state = 'draft'
deleted_move_ids.append(move.id)
move.unlink()
else:
invoice_move_no_line[move2inv[move.id]] = move.name
if deleted_move_ids:
logger.info(
'Account move IDs %s have been deleted because they '
'had 0 lines', deleted_move_ids)
else:
logger.info('0 moves without lines found')
if invoice_move_no_line:
for inv_id, inv_number in invoice_move_no_line.iteritems():
logger.info(
'Invoice ID %d number %s has a move with 0 lines',
inv_id, inv_number)
class AccountMoveLine(models.Model): class AccountMoveLine(models.Model):
_inherit = 'account.move.line' _inherit = 'account.move.line'
@@ -163,6 +305,28 @@ class AccountMoveLine(models.Model):
else: else:
self.credit = amount_company_currency self.credit = amount_company_currency
analytic_account_id = fields.Many2one(
domain=[('type', 'not in', ('view', 'template'))])
class AccountBankStatement(models.Model):
_inherit = 'account.bank.statement'
start_date = fields.Date(
compute='_compute_dates', string='Start Date', readonly=True,
store=True)
end_date = fields.Date(
compute='_compute_dates', string='End Date', readonly=True,
store=True)
@api.multi
@api.depends('line_ids.date')
def _compute_dates(self):
for st in self:
dates = [line.date for line in st.line_ids]
st.start_date = dates and min(dates) or False
st.end_date = dates and max(dates) or False
class AccountBankStatementLine(models.Model): class AccountBankStatementLine(models.Model):
_inherit = 'account.bank.statement.line' _inherit = 'account.bank.statement.line'
@@ -210,6 +374,7 @@ class AccountBankStatementLine(models.Model):
raise UserError(_( raise UserError(_(
'No journal entry linked to this bank statement line.')) 'No journal entry linked to this bank statement line.'))
class ResPartner(models.Model): class ResPartner(models.Model):
_inherit = 'res.partner' _inherit = 'res.partner'

View File

@@ -38,6 +38,11 @@
<field name="period_id" position="attributes"> <field name="period_id" position="attributes">
<attribute name="groups"></attribute> <attribute name="groups"></attribute>
</field> </field>
<!-- move sent field and make it visible -->
<field name="sent" position="replace"/>
<field name="move_id" position="after">
<field name="sent"/>
</field>
</field> </field>
</record> </record>
@@ -61,6 +66,123 @@
<filter name="unpaid" position="after"> <filter name="unpaid" position="after">
<filter name="overdue" string="Overdue" <filter name="overdue" string="Overdue"
domain="[('state', '=', 'open'), ('date_due', '&lt;', current_date)]"/> domain="[('state', '=', 'open'), ('date_due', '&lt;', current_date)]"/>
<separator/>
<filter name="to_send" string="To Send" domain="[('sent', '=', False), ('state', 'in', ('open', 'paid'))]"/>
<filter name="sent" string="Sent" domain="[('sent', '=', True)]"/>
<separator/>
<filter name="no_attachment" string="Missing Attachment" domain="[('has_attachment', '=', False)]"/>
</filter>
</field>
</record>
<!-- Having a menu entry on invoice lines is often very usefull for odoo user:
they can search in their lines, etc...
So I enhance the generic views and add actions, but I don't add menu entries here ;
the creation of the corresponding menu entry should be done in the customer-specific
module -->
<record id="view_invoice_line_tree" model="ir.ui.view">
<field name="name">account_usability.invoice_line_tree</field>
<field name="model">account.invoice.line</field>
<field name="inherit_id" ref="account.view_invoice_line_tree"/>
<field name="arch" type="xml">
<field name="name" position="before">
<field name="partner_id" invisible="not context.get('show_invoice_fields')"/>
<field name="date_invoice" invisible="not context.get('show_invoice_fields')"/>
<field name="invoice_number" invisible="not context.get('show_invoice_fields')"/>
</field>
<field name="price_subtotal" position="after">
<field name="currency_id"/>
<field name="state" invisible="not context.get('show_invoice_fields')"/>
<field name="invoice_type" invisible="1"/>
</field>
<xpath expr="/tree" position="attributes">
<attribute name="edit">0</attribute>
<attribute name="create">0</attribute>
</xpath>
</field>
</record>
<record id="account_invoice_line_search" model="ir.ui.view">
<field name="name">account_usability.invoice_line_search</field>
<field name="model">account.invoice.line</field>
<field name="arch" type="xml">
<search string="Search Invoice Lines">
<field name="partner_id"/>
<field name="product_id"/>
<field name="account_id"/>
<field name="invoice_number"/>
<field name="name"/>
<filter name="out_invoice" string="Customer Invoices"
domain="[('invoice_type', '=', 'out_invoice')]"/>
<filter name="out_refund" string="Customer Refunds"
domain="[('invoice_type', '=', 'out_refund')]"/>
<filter name="in_invoice" string="Supplier Invoices"
domain="[('invoice_type', '=', 'in_invoice')]"/>
<filter name="in_refund" string="Supplier Refunds"
domain="[('invoice_type', '=', 'in_refund')]"/>
<separator/>
<filter name="draft" string="Draft" domain="[('state', '=', 'draft')]"/>
<filter name="unpaid" string="Not Paid" domain="[('state', '=', 'open')]"/>
<filter name="paid" string="Paid" domain="[('state', '=', 'paid')]"/>
<group string="Group By" name="groupby">
<filter name="partner_groupby" string="Partner"
context="{'group_by': 'partner_id'}"/>
<filter name="commercial_partner_group_by" string="Commercial Partner"
context="{'group_by': 'commercial_partner_id'}"/>
<filter name="date_groupby" string="Invoice Date"
context="{'group_by': 'date_invoice'}"/>
<filter name="product_groupby" string="Product"
context="{'group_by': 'product_id'}"/>
<filter name="account_groupby" string="Account"
context="{'group_by': 'account_id'}"/>
</group>
</search>
</field>
</record>
<record id="out_invoice_line_action" model="ir.actions.act_window">
<field name="name">Customer Invoice Lines</field>
<field name="res_model">account.invoice.line</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('invoice_type', '=', 'out_invoice')]</field>
<field name="context">{'show_invoice_fields': True}</field>
</record>
<record id="out_refund_line_action" model="ir.actions.act_window">
<field name="name">Customer Refund Lines</field>
<field name="res_model">account.invoice.line</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('invoice_type', '=', 'out_refund')]</field>
<field name="context">{'show_invoice_fields': True}</field>
</record>
<record id="in_invoice_line_action" model="ir.actions.act_window">
<field name="name">Supplier Invoice Lines</field>
<field name="res_model">account.invoice.line</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('invoice_type', '=', 'in_invoice')]</field>
<field name="context">{'show_invoice_fields': True}</field>
</record>
<record id="in_refund_line_action" model="ir.actions.act_window">
<field name="name">Supplier Refund Lines</field>
<field name="res_model">account.invoice.line</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('invoice_type', '=', 'in_refund')]</field>
<field name="context">{'show_invoice_fields': True}</field>
</record>
<record id="view_account_invoice_report_search" model="ir.ui.view">
<field name="name">usability.account.invoice.report.search</field>
<field name="model">account.invoice.report</field>
<field name="inherit_id" ref="account.view_account_invoice_report_search"/>
<field name="arch" type="xml">
<field name="categ_id" position="after">
<field name="product_id"/>
</field>
<filter name="category_product" position="after">
<filter string="Product" name="product_group_by" context="{'group_by': 'product_id'}"/>
</filter> </filter>
</field> </field>
</record> </record>
@@ -68,10 +190,24 @@
<!-- model account.move.line / Journal Items --> <!-- model account.move.line / Journal Items -->
<record id="account.action_account_moves_all_a" model="ir.actions.act_window"> <record id="account.action_account_moves_all_a" model="ir.actions.act_window">
<field name="limit">200</field> <field name="limit">200</field>
<!-- add graph -->
<field name="view_mode">tree_account_move_line_quickadd,form,graph</field>
<!-- Win space, because there are already many columns --> <!-- Win space, because there are already many columns -->
<field name="context">{'journal_show_code_only': True}</field> <field name="context">{'journal_show_code_only': True}</field>
</record> </record>
<record id="account_move_line_graph" model="ir.ui.view">
<field name="name">usability.account.move.line.graph</field>
<field name="model">account.move.line</field>
<field name="inherit_id" ref="account.account_move_line_graph"/>
<field name="arch" type="xml">
<graph position="attributes">
<!-- pivot by default instead of bar -->
<attribute name="type">pivot</attribute>
</graph>
</field>
</record>
<!-- model account.move / Journal Entries --> <!-- model account.move / Journal Entries -->
<record id="account.action_move_journal_line" model="ir.actions.act_window"> <record id="account.action_move_journal_line" model="ir.actions.act_window">
<field name="limit">200</field> <field name="limit">200</field>
@@ -101,6 +237,7 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="partner_id" position="after"> <field name="partner_id" position="after">
<field name="reconcile_ref" /> <field name="reconcile_ref" />
<field name="debit" filter_domain="['|', ('debit', '=', self), ('credit', '=', self)]" string="Debit or Credit"/>
</field> </field>
<filter name="unreconciled" position="before"> <filter name="unreconciled" position="before">
<filter name="reconciled" string="Fully Reconciled" domain="[('reconcile_id', '!=', False)]"/> <filter name="reconciled" string="Fully Reconciled" domain="[('reconcile_id', '!=', False)]"/>
@@ -128,6 +265,7 @@
<field name="name">account_usability.account_move_line_tree</field> <field name="name">account_usability.account_move_line_tree</field>
<field name="model">account.move.line</field> <field name="model">account.move.line</field>
<field name="inherit_id" ref="account.view_move_line_tree"/> <field name="inherit_id" ref="account.view_move_line_tree"/>
<field name="priority">1</field> <!-- to be compatible with account_analytic_plans which removes the field analytic_account_id and has a priority of 2 -->
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="analytic_account_id" position="attributes"> <field name="analytic_account_id" position="attributes">
<attribute name="invisible"></attribute> <attribute name="invisible"></attribute>
@@ -179,9 +317,10 @@
</button> </button>
</button> </button>
<button name="%(account.action_account_moves_all_tree)d" position="attributes"> <button name="%(account.action_account_moves_all_tree)d" position="attributes">
<attribute name="type">object</attribute> <attribute name="invisible">True</attribute>
<attribute name="name">show_receivable_account</attribute> </button>
<attribute name="attrs">{'invisible': [('customer', '=', False)]}</attribute> <button name="%(account.action_account_moves_all_tree)d" position="after">
<button type="object" class="oe_stat_button" name="show_receivable_account" icon="fa-list" attrs="{'invisible': [('customer', '=', False)]}"/>
</button> </button>
<field name="journal_item_count" position="attributes"> <field name="journal_item_count" position="attributes">
<attribute name="string">Receivable Account</attribute> <attribute name="string">Receivable Account</attribute>
@@ -189,6 +328,12 @@
</field> </field>
</record> </record>
<!-- When you click on the "Contract" button of partner form view,
the newly created analytic accounts should be of type contract ! -->
<record id="account.action_open_partner_analytic_accounts" model="ir.actions.act_window">
<field name="context">{'search_default_partner_id': [active_id], 'default_partner_id': active_id, 'default_type': 'contract'}</field>
</record>
<record id="view_bank_statement_form" model="ir.ui.view"> <record id="view_bank_statement_form" model="ir.ui.view">
<field name="name">usability.account.bank.statement.form</field> <field name="name">usability.account.bank.statement.form</field>
<field name="model">account.bank.statement</field> <field name="model">account.bank.statement</field>
@@ -200,6 +345,65 @@
string="View Account Move" icon="gtk-redo" string="View Account Move" icon="gtk-redo"
attrs="{'invisible': [('journal_entry_id', '=', False)]}"/> attrs="{'invisible': [('journal_entry_id', '=', False)]}"/>
</xpath> </xpath>
<field name="company_id" position="before">
<field name="start_date"/>
<field name="end_date"/>
</field>
</field>
</record>
<record id="view_bank_statement_tree" model="ir.ui.view">
<field name="name">usability.account.bank.statement.tree</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_tree"/>
<field name="arch" type="xml">
<field name="period_id" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<field name="date" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<field name="journal_id" position="after">
<field name="start_date"/>
<field name="end_date"/>
</field>
</field>
</record>
<record id="view_bank_statement_search" model="ir.ui.view">
<field name="name">usability.account.bank.statement.search</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_search"/>
<field name="arch" type="xml">
<field name="date" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<filter context="{'group_by': 'period_id'}" position="attributes">
<attribute name="invisible">1</attribute>
</filter>
<filter context="{'group_by': 'period_id'}" position="after">
<filter name="start_date_groupby" string="Start Date"
context="{'group_by': 'start_date'}"/>
<filter name="end_date_groupby" string="End Date"
context="{'group_by': 'end_date'}"/>
</filter>
<field name="date" position="after">
<field name="start_date"/>
<field name="end_date"/>
</field>
</field>
</record>
<record id="view_account_analytic_account_form" model="ir.ui.view">
<field name="name">analytic.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">
<notebook position="inside">
<page name="invoice_lines" string="Related Invoice Lines" attrs="{'invisible': [('type', 'not in', ('contract', 'normal'))]}">
<field name="invoice_line_ids" nolabel="1" context="{'show_invoice_fields': 1}"/>
</page>
</notebook>
</field> </field>
</record> </record>

View File

@@ -0,0 +1,3 @@
# -*- encoding: utf-8 -*-
from . import account_invoice_mark_sent

View File

@@ -0,0 +1,23 @@
# -*- 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 openerp import models, api
import logging
logger = logging.getLogger(__name__)
class AccountInvoiceMarkSent(models.TransientModel):
_name = 'account.invoice.mark.sent'
_description = 'Mark invoices as sent'
@api.multi
def run(self):
assert self.env.context.get('active_model') == 'account.invoice',\
'Source model must be invoices'
assert self.env.context.get('active_ids'), 'No invoices selected'
invoices = self.env['account.invoice'].browse(
self.env.context.get('active_ids'))
invoices.write({'sent': True})
logger.info('Marking invoices with ID %s as sent', invoices.ids)
return

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
The licence is in the file __openerp__.py
-->
<openerp>
<data>
<record id="account_invoice_mark_sent_form" model="ir.ui.view">
<field name="name">account.invoice.mark.sent.form</field>
<field name="model">account.invoice.mark.sent</field>
<field name="arch" type="xml">
<form string="Mark invoices as sent">
<p>
This wizard will mark as sent all the selected invoices.
</p>
<footer>
<button type="object" name="run" string="Mark as Sent" class="oe_highlight"/>
<button special="cancel" string="Cancel" class="oe_link"/>
</footer>
</form>
</field>
</record>
<act_window id="account_invoice_mark_sent_action"
multi="True"
key2="client_action_multi"
name="Mark as Sent"
res_model="account.invoice.mark.sent"
src_model="account.invoice"
view_mode="form"
target="new" />
</data>
</openerp>

View File

@@ -7,8 +7,45 @@
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
</record> </record>
<record id="product_attribute_view_form" model="ir.ui.view">
<field name="model">product.attribute</field>
<field name="arch" type="xml">
<form string="Product Attribute">
<sheet>
<div class="oe_title" style="width: 390px;">
<label class="oe_edit_only" for="name" string="Product Name"/>
<h1><field name="name" class="oe_inline"/></h1>
</div>
<group>
<field name="value_ids" colspan="4" nolabel="1"
context="{'hide_attribute_id': True}"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="attribute_tree_view" model="ir.ui.view">
<field name="model">product.attribute</field>
<field name="inherit_id" ref="product.attribute_tree_view"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="editable"></attribute>
</xpath>
</field>
</record>
<record id="variants_tree_view" model="ir.ui.view">
<field name="model">product.attribute.value</field>
<field name="inherit_id" ref="product.variants_tree_view"/>
<field name="arch" type="xml">
<field name="attribute_id" position="attributes">
<attribute name="invisible">context.get('hide_attribute_id')</attribute>
</field>
</field>
</record>
<record id="product.attribute_action" model="ir.actions.act_window"> <record id="product.attribute_action" model="ir.actions.act_window">
<field name="res_model">product.attribute</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
</record> </record>

View File

@@ -12,6 +12,9 @@
<field name="model">ir.module.module</field> <field name="model">ir.module.module</field>
<field name="inherit_id" ref="base.view_module_filter"/> <field name="inherit_id" ref="base.view_module_filter"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//filter[@name='extra']" position="after">
<filter name="installable" string="Installable" domain="[('state', '!=', 'uninstallable')]"/>
</xpath>
<xpath expr="//filter[@string='Category']" position="after"> <xpath expr="//filter[@string='Category']" position="after">
<filter string="State" domain="[]" context="{'group_by':'state'}"/> <filter string="State" domain="[]" context="{'group_by':'state'}"/>
</xpath> </xpath>
@@ -20,7 +23,7 @@
<record id="base.open_module_tree" model="ir.actions.act_window"> <record id="base.open_module_tree" model="ir.actions.act_window">
<field name="res_model">ir.module.module</field> <field name="res_model">ir.module.module</field>
<field name="context">{}</field> <field name="context">{'search_default_installable': 1}</field>
</record> </record>
</data> </data>

View File

@@ -60,3 +60,21 @@ class Partner(models.Model):
else: else:
name_title = ' '.join([title, name_title]) name_title = ' '.join([title, name_title])
self.name_title = name_title self.name_title = name_title
@api.model
def name_search(self, name, args=None, operator='ilike', limit=100):
if args is None:
args = []
if name:
# only filter on name and ref not in email
args += [
'|', ('display_name', 'ilike', name), ('ref', 'ilike', name)]
res = super(Partner, self).name_search(
name, args=args, operator=operator, limit=limit)
return [(pid, val.replace('\n', ' ')) for pid, val in res]
class ResPartnerCategory(models.Model):
_inherit = 'res.partner.category'
name = fields.Char(translate=False)

View File

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

View File

@@ -5,7 +5,7 @@
{ {
'name': 'CRM Usability', 'name': 'CRM Usability',
'version': '8.0.1.0.0', 'version': '8.0.1.0.1',
'category': 'Customer Relationship Management', 'category': 'Customer Relationship Management',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'CRM usability enhancements', 'summary': 'CRM usability enhancements',
@@ -13,10 +13,12 @@
CRM Usability CRM Usability
============= =============
Some enhancements in the *Merge Partners* wizard: The usability improvements include :
* take into account the unaccent option of the server config file * Adds multi-company record rules on crm.lead.
* add optional group by on 'customer' and 'supplier' (active by default) * Some enhancements in the *Merge Partners* wizard:
* take into account the unaccent option of the server config file
* add optional group by on 'customer' and 'supplier' (active by default)
This module has been written by Alexis de Lattre from Akretion This module has been written by Alexis de Lattre from Akretion
<alexis.delattre@akretion.com>. <alexis.delattre@akretion.com>.
@@ -24,6 +26,10 @@ This module has been written by Alexis de Lattre from Akretion
'author': 'Akretion', 'author': 'Akretion',
'website': 'http://www.akretion.com', 'website': 'http://www.akretion.com',
'depends': ['crm'], 'depends': ['crm'],
'data': ['wizard/base_partner_merge_view.xml'], 'data': [
'crm_view.xml',
'wizard/base_partner_merge_view.xml',
'security/rule.xml',
],
'installable': True, 'installable': True,
} }

12
crm_usability/crm.py Normal file
View File

@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# © 2017 Akretion (http://www.akretion.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
# @author Alexis de Lattre <alexis.delattre@akretion.com>
from openerp import models, fields
class CrmCaseCateg(models.Model):
_inherit = 'crm.case.categ'
name = fields.Char(translate=False)

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 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).
-->
<openerp>
<data>
<record id="crm_case_form_view_oppor" model="ir.ui.view">
<field name="name">usability.crm.opportunity.form</field>
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_case_form_view_oppor"/>
<field name="arch" type="xml">
<field name="active" position="before">
<field name="company_id" groups="base.group_multi_company"
widget="selection"/>
</field>
</field>
</record>
<record id="crm_case_inbound_phone_tree_view" model="ir.ui.view">
<field name="name">crm_usability.crm.phonecall.tree.inbound</field>
<field name="model">crm.phonecall</field>
<field name="inherit_id" ref="crm.crm_case_inbound_phone_tree_view"/>
<field name="arch" type="xml">
<field name="state" position="attributes">
<attribute name="invisible">0</attribute>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="crm_lead_company_rule" model="ir.rule">
<field name="name">Lead/opportunity multi-company rule</field>
<field name="model_id" ref="crm.model_crm_lead"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Copyright 2017 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).
{
"name": "Delivery usability",
"summary": "Delivery Usability",
"version": "8.0.1.0.0",
"category": "Warehouse",
"website": "www.akretion.com",
"author": " Akretion",
"license": "AGPL-3",
"application": False,
"installable": True,
"external_dependencies": {
"python": [],
"bin": [],
},
"depends": [
"delivery",
],
"data": [
"views/stock_picking_view.xml",
],
"demo": [
],
"qweb": [
]
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="view_picking_withcarrier_out_form" model="ir.ui.view">
<field name="model">stock.picking</field>
<field name="inherit_id" ref="delivery.view_picking_withcarrier_out_form"/>
<field name="priority" eval="100"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='carrier_id']/.." position="replace">
</xpath>
<field name="backorder_id" position="after">
<field name="carrier_id"/>
<field name="carrier_tracking_ref"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from . import company
from . import hr_holidays
from . import hr_employee
from . import lunch_voucher_attribution
from . import wizard

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# © 2017 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': 'HR Holidays Lunch Voucher',
'version': '10.0.1.0.0',
'category': 'Human Resources',
'license': 'AGPL-3',
'summary': 'Manage lunch vouchers in holidays requests',
'description': '',
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['hr_holidays_usability', 'purchase'],
'data': [
'security/ir.model.access.csv',
'security/lunch_voucher_security.xml',
'product_data.xml',
'company_view.xml',
'wizard/lunch_voucher_purchase_view.xml',
'lunch_voucher_attribution_view.xml',
'hr_employee_view.xml',
'hr_holidays_view.xml',
'wizard/hr_holidays_post_view.xml',
],
'installable': True,
}

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# © 2017 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 openerp import models, fields
import openerp.addons.decimal_precision as dp
class ResCompany(models.Model):
_inherit = 'res.company'
lunch_voucher_product_id = fields.Many2one(
'product.product', string='Lunch Voucher Product',
ondelete='restrict')
lunch_voucher_employer_price = fields.Float(
'Lunch Voucher Employer Price', digits=dp.get_precision('Account'))
# Add constrain to check that lunch_voucher_employer_price is between
# 50% and 60% of the price of lunch_voucher_product_id for France

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 Akretion France (www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<openerp>
<data>
<record id="view_company_form" model="ir.ui.view">
<field name="name">lunch_voucher.company.form</field>
<field name="model">res.company</field>
<field name="inherit_id" ref="base.view_company_form"/>
<field name="arch" type="xml">
<group name="account_grp" position="after">
<group name="lunch_voucher" string="Lunch Vouchers">
<field name="lunch_voucher_product_id"/>
<!-- <field name="lunch_voucher_po_type"/> -->
<field name="lunch_voucher_employer_price"/>
</group>
</group>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# © 2017 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 openerp import models, fields
class HrEmployee(models.Model):
_inherit = 'hr.employee'
lunch_voucher = fields.Boolean(string="Has Lunch Vouchers", default=True)

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 Akretion France (www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<openerp>
<data>
<record id="view_employee_form_leave_inherit" model="ir.ui.view">
<field name="name">lunch_voucher.hr.employee.form</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr_holidays_usability.view_employee_form_leave_inherit"/>
<field name="arch" type="xml">
<group name="holidays" position="inside">
<field name="lunch_voucher"/>
</group>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# © 2017 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 openerp import models, fields, api
from dateutil.relativedelta import relativedelta
class HrHolidays(models.Model):
_inherit = 'hr.holidays'
lunch_voucher_id = fields.Many2one(
'lunch.voucher.attribution',
string="Related Lunch Voucher Attribution")
lunch_voucher_remove_qty = fields.Integer(
compute='_compute_lunch_voucher_remove_qty', readonly=True,
store=True, string='Lunch Vouchers to Deduct')
@api.depends('employee_id', 'vacation_date_from', 'vacation_date_to')
@api.multi
def _compute_lunch_voucher_remove_qty(self):
hhpo = self.env['hr.holidays.public']
for hol in self:
qty = 0
if (
hol.type == 'remove' and
hol.vacation_date_from and
hol.vacation_date_to):
start_date_dt = fields.Date.from_string(hol.vacation_date_from)
end_date_str = hol.vacation_date_to
date_dt = start_date_dt
# Remove 1 full LV when vacation_time_from == noon
# and also when vacation_time_to == noon
while True:
if (
date_dt.weekday() not in (5, 6) and
not hhpo.is_public_holiday(
date_dt, hol.employee_id.id)):
qty += 1
date_dt += relativedelta(days=1)
date_str = fields.Date.to_string(date_dt)
if date_str > end_date_str:
break
hol.lunch_voucher_remove_qty = qty

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 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).
-->
<openerp>
<data>
<record id="edit_holiday_new" model="ir.ui.view">
<field name="name">hr_holidays_lunch_voucher.leave_request_form</field>
<field name="model">hr.holidays</field>
<field name="inherit_id" ref="hr_holidays_usability.edit_holiday_new"/>
<field name="arch" type="xml">
<group name="counters" position="after">
<group name="lunch_vouchers" string="Lunch Vouchers" attrs="{'invisible': [('type', '!=', 'remove')]}">
<field name="lunch_voucher_remove_qty"/>
<field name="lunch_voucher_id"/>
</group>
</group>
</field>
</record>
<record id="view_holiday" model="ir.ui.view">
<field name="name">hr_holidays_lunch_voucher.leave_request_tree</field>
<field name="model">hr.holidays</field>
<field name="inherit_id" ref="hr_holidays_usability.view_holiday"/>
<field name="arch" type="xml">
<field name="number_of_days" position="after">
<field name="lunch_voucher_remove_qty" string="Lunch Vouchers -=" sum="Total"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# © 2017 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 openerp import models, fields, api
class LunchVoucherAttribution(models.Model):
_name = 'lunch.voucher.attribution'
_description = 'Lunch Voucher Attribution'
_order = 'date desc'
employee_id = fields.Many2one(
'hr.employee', string='Employee', ondelete='restrict',
required=True, readonly=True)
company_id = fields.Many2one(
related='employee_id.resource_id.company_id', readonly=True,
store=True)
date = fields.Date('Attribution Date', readonly=True)
purchase_id = fields.Many2one(
'purchase.order', 'Purchase Order',
readonly=True)
month_work_days = fields.Integer(
'Month Work Days',
help="Number of work days of the month (without taking into "
"account the holidays)")
no_lunch_days = fields.Integer(
compute='_compute_qty', string='No Lunch Days',
readonly=True, store=True)
qty = fields.Integer(
compute='_compute_qty', readonly=True, store=True,
string='Lunch Voucher Quantity')
holiday_ids = fields.One2many(
'hr.holidays', 'lunch_voucher_id', readonly=True)
@api.depends('month_work_days', 'holiday_ids.lunch_voucher_remove_qty')
@api.multi
def _compute_qty(self):
for rec in self:
no_lunch_days = 0
for hol in rec.holiday_ids:
no_lunch_days += hol.lunch_voucher_remove_qty
rec.no_lunch_days = no_lunch_days
rec.qty = rec.month_work_days - no_lunch_days

View File

@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 Akretion France (www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<openerp>
<data>
<record id="lunch_voucher_attribution_form" model="ir.ui.view">
<field name="name">lunch.voucher.attribution.form</field>
<field name="model">lunch.voucher.attribution</field>
<field name="arch" type="xml">
<form string="Lunch Vouchers Attribution">
<group name="main">
<field name="employee_id"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="date"/>
<field name="month_work_days"/>
<field name="no_lunch_days"/>
<field name="qty"/>
<field name="purchase_id"/>
</group>
<group name="holidays" string="Related Holidays">
<field name="holiday_ids" nolabel="1" context="{'tree_view_ref': 'hr_holidays_lunch_voucher.view_holiday', 'form_view_ref': 'hr_holidays_lunch_voucher.edit_holiday_new'}"/>
</group>
</form>
</field>
</record>
<record id="lunch_voucher_attribution_tree" model="ir.ui.view">
<field name="name">lunch.voucher.attribution.tree</field>
<field name="model">lunch.voucher.attribution</field>
<field name="arch" type="xml">
<tree string="Lunch Vouchers Attribution">
<field name="date"/>
<field name="employee_id"/>
<field name="month_work_days"/>
<field name="no_lunch_days" sum="Total"/>
<field name="qty" sum="Total"/>
<field name="purchase_id"/>
<field name="company_id" groups="base.group_multi_company"/>
</tree>
</field>
</record>
<record id="lunch_voucher_attribution_search" model="ir.ui.view">
<field name="name">lunch.voucher.attribution.search</field>
<field name="model">lunch.voucher.attribution</field>
<field name="arch" type="xml">
<search string="Search Lunch Vouchers Attribution">
<field name="date"/>
<field name="employee_id"/>
<filter name="purchased" string="Purchased" domain="[('purchase_id', '!=', False)]"/>
<filter name="to_purchase" string="To Purchase" domain="[('purchase_id', '=', False)]"/>
<group string="Group By" name="groupby">
<filter name="employee_groupby" string="Employee" context="{'group_by': 'employee_id'}"/>
<filter name="purchase_order_groupby" string="Purchase Order" context="{'group_by': 'purchase_id'}"/>
</group>
</search>
</field>
</record>
<record id="lunch_voucher_attribution_graph" model="ir.ui.view">
<field name="name">lunch.voucher.attribution.graph</field>
<field name="model">lunch.voucher.attribution</field>
<field name="arch" type="xml">
<graph string="Lunch Vouchers Attribution" type="pivot">
<field name="date" type="row" interval="month"/>
<field name="qty" type="measure"/>
</graph>
</field>
</record>
<record id="lunch_voucher_attribution_action" model="ir.actions.act_window">
<field name="name">Lunch Voucher Attribution</field>
<field name="res_model">lunch.voucher.attribution</field>
<field name="view_mode">tree,form,graph</field>
</record>
<menuitem id="lunch_voucher_attribution_menu"
action="lunch_voucher_attribution_action"
parent="hr_holidays.menu_open_ask_holidays" sequence="200"/>
<act_window id="lunch_voucher_attribution_action_create_po"
multi="True"
key2="client_action_multi"
name="Create Purchase Order"
res_model="lunch.voucher.purchase"
src_model="lunch.voucher.attribution"
view_mode="form"
target="new" />
</data>
</openerp>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="lunch_voucher_product" model="product.product">
<field name="name">Lunch Vouchers</field>
<field name="categ_id" ref="product.product_category_all"/>
<field name="sale_ok" eval="False"/>
<field name="purchase_ok" eval="True"/>
<field name="list_price">0</field>
<field name="standard_price">8</field>
<field name="type">service</field> <!-- For those who want to manage stock of lunch vouchers, they can switch type to product manually -->
<field name="uom_id" ref="product.product_uom_unit"/>
<field name="uom_po_id" ref="product.product_uom_unit"/>
<field name="supplier_taxes_id" eval="False"/>
<field name="taxes_id" eval="False"/>
</record>
<record id="base.main_company" model="res.company">
<field name="lunch_voucher_product_id" ref="lunch_voucher_product"/>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_lunch_voucher_attribution_full,Full access on lunch_voucher_attribution,model_lunch_voucher_attribution,base.group_hr_manager,1,1,1,1
access_lunch_voucher_attribution_read,Read access on lunch_voucher_attribution,model_lunch_voucher_attribution,base.group_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_lunch_voucher_attribution_full Full access on lunch_voucher_attribution model_lunch_voucher_attribution base.group_hr_manager 1 1 1 1
3 access_lunch_voucher_attribution_read Read access on lunch_voucher_attribution model_lunch_voucher_attribution base.group_user 1 0 0 0

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="lunch_voucher_attribution_employee" model="ir.rule">
<field name="name">Personal Lunch Voucher Attributions</field>
<field name="model_id" ref="hr_holidays_lunch_voucher.model_lunch_voucher_attribution"/>
<field name="domain_force">[('employee_id.user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="lunch_voucher_attribution_officer" model="ir.rule">
<field name="name">HR Officer sees all Lunch Voucher Attributions</field>
<field name="model_id" ref="hr_holidays_lunch_voucher.model_lunch_voucher_attribution"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('base.group_hr_manager'))]"/>
</record>
<record id="lunch_voucher_attribution_multicompany_rule" model="ir.rule">
<field name="name">Lunch Voucher Attribution multi-company</field>
<field name="model_id" ref="hr_holidays_lunch_voucher.model_lunch_voucher_attribution"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,4 @@
# -*- encoding: utf-8 -*-
from . import hr_holidays_post
from . import lunch_voucher_purchase

View File

@@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
# © 2017 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 openerp import models, fields, api
from dateutil.relativedelta import relativedelta
import logging
logger = logging.getLogger(__name__)
class HrHolidaysPost(models.TransientModel):
_inherit = 'hr.holidays.post'
lunch_voucher_po = fields.Boolean(
string='Generate Lunch Voucher Purchase Order')
work_days = fields.Integer(string='Work Days')
@api.model
def default_get(self, fields_list):
res = super(HrHolidaysPost, self).default_get(fields_list)
hhpo = self.env['hr.holidays.public']
company = self.env.user.company_id
if company.lunch_voucher_product_id:
res['lunch_voucher_po'] = True
today = fields.Date.context_today(self)
today_dt = fields.Date.from_string(today)
cur_month = today_dt.month
# last day of month
date_dt = today_dt + relativedelta(day=31)
work_days = date_dt.day
logger.info('Number of days in the month: %d', work_days)
# from last day of month to the first
while date_dt.month == cur_month:
if hhpo.is_public_holiday(date_dt):
work_days -= 1
logger.info(
"%s is a bank holiday, don't count", date_dt)
# if it's a saturday/sunday
elif date_dt.weekday() in (5, 6):
work_days -= 1
logger.info(
"%s is a saturday/sunday, don't count", date_dt)
date_dt += relativedelta(days=-1)
logger.info('Number of work days in the month: %d', work_days)
res['work_days'] = work_days
return res
@api.multi
def run(self):
self.ensure_one()
lvao = self.env['lunch.voucher.attribution']
today = fields.Date.context_today(self)
employees = self.env['hr.employee'].search([
('lunch_voucher', '=', True),
('company_id', '=', self.env.user.company_id.id),
])
lv_dict = {}
for employee in employees:
lv_dict[employee.id] = []
for hol in self.holidays_to_post_ids:
if not hol.lunch_voucher_id and hol.employee_id.id in lv_dict:
lv_dict[hol.employee_id.id].append(hol.id)
for employee_id, hol_ids in lv_dict.iteritems():
vals = {
'employee_id': employee_id,
'date': today,
'month_work_days': self.work_days,
}
if hol_ids:
vals['holiday_ids'] = [(6, 0, hol_ids)]
lvao.create(vals)
return super(HrHolidaysPost, self).run()

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 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).
-->
<openerp>
<data>
<record id="hr_holidays_post_form" model="ir.ui.view">
<field name="name">hr_holidays_post_form</field>
<field name="model">hr.holidays.post</field>
<field name="inherit_id" ref="hr_holidays_usability.hr_holidays_post_form"/>
<field name="arch" type="xml">
<field name="holidays_to_post_ids" position="before">
<field name="lunch_voucher_po" states="done"/>
<field name="work_days"
attrs="{'invisible': ['|', ('state', '!=', 'done'), ('lunch_voucher_po', '=', False)]}"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
# © 2017 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 openerp import models, api, _
from openerp.exceptions import Warning as UserError
class LunchVoucherPurchase(models.TransientModel):
_name = 'lunch.voucher.purchase'
_description = 'Purchase Lunch Vouchers Wizard'
@api.multi
def run(self):
self.ensure_one()
company = self.env.user.company_id
if not company.lunch_voucher_product_id:
raise UserError(_(
"Lunch Voucher Product not configured on company %s")
% company.name)
if not company.lunch_voucher_product_id.seller_id:
raise UserError(_(
"Missing supplier on Product '%s'.")
% company.lunch_voucher_product_id.name)
poo = self.env['purchase.order']
polo = self.env['purchase.order.line']
lvao = self.env['lunch.voucher.attribution']
assert self._context.get('active_model') ==\
'lunch.voucher.attribution', 'wrong source model'
assert self._context.get('active_ids'), 'missing active_ids in ctx'
lvouchers = lvao.browse(self._context['active_ids'])
total_qty = 0
for lvoucher in lvouchers:
if lvoucher.purchase_id:
raise UserError(_(
"One of the Lunch Voucher Attributions you selected "
"related to employee '%s' is already linked to a "
"purchase order.") % lvoucher.employee_id.name)
if lvoucher.qty < 0:
raise UserError(_(
"One of the Lunch Voucher Attributions you selected "
"related to employee '%s' has a negative quantity.")
% lvoucher.employee_id.name)
total_qty += lvoucher.qty
supplier = company.lunch_voucher_product_id.seller_id
pick_type_id = poo.default_get(['picking_type_id'])['picking_type_id']
onchange_ptype_vals = poo.browse(False).onchange_picking_type_id(
pick_type_id)
vals = onchange_ptype_vals['value']
onchange_vals = poo.browse(False).onchange_partner_id(supplier.id)
vals.update(onchange_vals['value'])
vals['partner_id'] = supplier.id
product = company.lunch_voucher_product_id
onchange_product_vals = polo.browse(False).onchange_product_id(
vals.get('pricelist_id'), product.id, total_qty, False,
supplier.id, fiscal_position_id=vals.get('fiscal_position_id'))
lvals = onchange_product_vals['value']
lvals['product_id'] = product.id
lvals['product_qty'] = total_qty
if lvals['taxes_id']:
lvals['taxes_id'] = [(6, 0, lvals['taxes_id'])]
vals['order_line'] = [(0, 0, lvals)]
po = poo.create(vals)
lvouchers.write({'purchase_id': po.id})
action = self.env['ir.actions.act_window'].for_xml_id(
'purchase', 'purchase_rfq')
action.update({
'res_id': po.id,
'view_mode': 'form,tree',
'views': False,
})
return action

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 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).
-->
<openerp>
<data>
<record id="lunch_voucher_purchase_form" model="ir.ui.view">
<field name="name">lunch_voucher_purchase_form</field>
<field name="model">lunch.voucher.purchase</field>
<field name="arch" type="xml">
<form string="Create Purchase Order of Lunch Vouchers">
<group name="main">
<label string="Click on the button below to create the purchase order for the selected lunch vouchers attributions." colspan="2"/>
</group>
<footer>
<button name="run" type="object" string="Create"
class="oe_highlight"/>
<button special="cancel" string="Cancel" class="oe_link"/>
</footer>
</form>
</field>
</record>
<record id="lunch_voucher_purchase_action" model="ir.actions.act_window">
<field name="name">Create PO for Lunch Vouchers</field>
<field name="res_model">lunch.voucher.purchase</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

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

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# © 2017 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': 'HR Holidays Lunch Voucher Natixis',
'version': '10.0.1.0.0',
'category': 'Human Resources',
'license': 'AGPL-3',
'summary': 'Generate order file for Natixis lunch vouchers',
'description': '',
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['hr_holidays_lunch_voucher'],
'data': [
'company_view.xml',
],
'installable': True,
}

View File

@@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
# © 2017 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 openerp import models, fields
class ResCompany(models.Model):
_inherit = 'res.company'
lunch_voucher_natixis_customer_code = fields.Char(
string='Natixis Customer Ref', size=7)
lunch_voucher_natixis_delivery_code = fields.Char(
string='Natixis Delivery Code', size=7)

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 Akretion France (www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<openerp>
<data>
<record id="view_company_form" model="ir.ui.view">
<field name="name">natixis.lunch_voucher.company.form</field>
<field name="model">res.company</field>
<field name="inherit_id" ref="hr_holidays_lunch_voucher.view_company_form"/>
<field name="arch" type="xml">
<group name="lunch_voucher" position="inside">
<field name="lunch_voucher_natixis_customer_code"/>
<field name="lunch_voucher_natixis_delivery_code"/>
</group>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,3 @@
# -*- encoding: utf-8 -*-
from . import lunch_voucher_purchase

View File

@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
# © 2017 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 openerp import models, fields, api, _
from openerp.exceptions import Warning as UserError
from openerp.tools import float_is_zero
class LunchVoucherPurchase(models.TransientModel):
_inherit = 'lunch.voucher.purchase'
@api.multi
def run(self):
self.ensure_one()
action = super(LunchVoucherPurchase, self).run()
company = self.env.user.company_id
# force_company to read standard_price properly
company = company.with_context(force_company=company.id)
lvao = self.env['lunch.voucher.attribution']
assert self._context.get('active_model')\
== 'lunch.voucher.attribution', 'wrong source model'
assert self._context.get('active_ids'), 'missing active_ids in ctx'
if not company.lunch_voucher_natixis_customer_code:
raise UserError(_(
"Missing Natixis Customer Ref on company '%s'.")
% company.name)
if not company.lunch_voucher_natixis_delivery_code:
raise UserError(_(
"Missing Natixis Delivery Code on company '%s'.")
% company.name)
if len(company.lunch_voucher_natixis_customer_code) != 7:
raise UserError(_(
"Natixis Customer Ref '%s' on company '%s' should "
"have 7 characters/digits.")
% (company.lunch_voucher_natixis_customer_code, company.name))
if len(company.lunch_voucher_natixis_delivery_code) != 7:
raise UserError(_(
"Natixis Delivery Code on company '%s' should "
"have 7 characters/digits.")
% (company.lunch_voucher_natixis_delivery_code, company.name))
if float_is_zero(
company.lunch_voucher_employer_price,
precision_digits=2):
raise UserError(_(
"Lunch Voucher Employer Price not set on company '%s'.")
% company.name)
if float_is_zero(
company.lunch_voucher_product_id.standard_price,
precision_digits=2):
raise UserError(_(
"Lunch Voucher Standard Price is not set on product '%s'.")
% company.lunch_voucher_product_id.display_name)
lvouchers = lvao.browse(self._context['active_ids'])
of = u''
tmp = {}
for lvoucher in lvouchers:
if lvoucher.qty > 0:
if lvoucher.qty not in tmp:
tmp[lvoucher.qty] = 1
else:
tmp[lvoucher.qty] += 1
for vouchers_per_pack, pack_qty in tmp.iteritems():
if vouchers_per_pack > 99:
raise UserError(_(
"Cannot order more than 99 vouchers per pack"))
line = u'%s%s%s%s%s%s%s%s\n' % (
company.lunch_voucher_natixis_delivery_code,
company.lunch_voucher_natixis_customer_code,
unicode(pack_qty).zfill(3),
unicode(vouchers_per_pack).zfill(2),
unicode(pack_qty * vouchers_per_pack).zfill(5),
'{:05.2f}'.format(
company.lunch_voucher_product_id.standard_price),
'{:05.2f}'.format(company.lunch_voucher_employer_price),
' ' * 64)
of += line
today_dt = fields.Date.from_string(
fields.Date.context_today(self))
filename = 'E%s_%s.txt' % (
company.lunch_voucher_natixis_customer_code,
today_dt.strftime('%d%m%Y'))
self.env['ir.attachment'].create({
'name': filename,
'res_id': action['res_id'],
'res_model': 'purchase.order',
'datas': of.encode('base64'),
'datas_fname': filename,
'type': 'binary',
})
return action

View File

@@ -241,6 +241,10 @@ class HrHolidays(models.Model):
string='Public Title', string='Public Title',
help="Warning: this title is shown publicly in the " help="Warning: this title is shown publicly in the "
"calendar. Don't write private/personnal information in this field.") "calendar. Don't write private/personnal information in this field.")
# 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)
@api.one @api.one
@api.constrains( @api.constrains(

View File

@@ -61,6 +61,7 @@ hr_holidays.edit_holiday_new is used for both leaves and allocation -->
</group> </group>
</field> </field>
<field name="department_id" position="after"> <field name="department_id" position="after">
<field name="company_id" groups="base.group_multi_company"/>
<field name="posted_date" groups="base.group_hr_manager"/> <field name="posted_date" groups="base.group_hr_manager"/>
</field> </field>
</field> </field>
@@ -90,6 +91,7 @@ hr_holidays.edit_holiday_new is used for both leaves and allocation -->
</field> </field>
<field name="holiday_status_id" position="after"> <field name="holiday_status_id" position="after">
<field name="posted_date" groups="base.group_hr_manager"/> <field name="posted_date" groups="base.group_hr_manager"/>
<field name="company_id" groups="base.group_multi_company"/>
</field> </field>
</field> </field>
</record> </record>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data noupdate="0"> <data noupdate="1">
<!-- <!--
Employee : only see his holidays Employee : only see his holidays
@@ -41,5 +41,12 @@ Manager = person that administrates the holidays process : can see everything, d
<field name="groups" eval="[(4, ref('base.group_hr_manager'))]"/> <field name="groups" eval="[(4, ref('base.group_hr_manager'))]"/>
</record> </record>
<record id="hr_holidays_multicompany_rule" model="ir.rule">
<field name="name">Holidays multi-company</field>
<field name="model_id" ref="hr_holidays.model_hr_holidays"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
</record>
</data> </data>
</openerp> </openerp>

View File

@@ -31,7 +31,9 @@ class HrHolidaysMassAllocation(models.TransientModel):
@api.model @api.model
def _default_employees(self): def _default_employees(self):
return self.env['hr.employee'].search([ return self.env['hr.employee'].search([
('holiday_exclude_mass_allocation', '=', False)]) ('holiday_exclude_mass_allocation', '=', False),
('company_id', '=', self.env.user.company_id.id),
])
@api.model @api.model
def _get_default_holiday_status(self): def _get_default_holiday_status(self):

View File

@@ -56,6 +56,7 @@ class HrHolidaysPost(models.TransientModel):
('state', '=', 'validate'), ('state', '=', 'validate'),
('posted_date', '=', False), ('posted_date', '=', False),
('vacation_date_to', '<=', self.before_date), ('vacation_date_to', '<=', self.before_date),
('company_id', '=', self.env.user.company_id.id),
]) ])
self.write({ self.write({
'state': 'done', 'state': 'done',
@@ -73,9 +74,11 @@ class HrHolidaysPost(models.TransientModel):
# because, after the write, it doesn't have a value any more !!! # because, after the write, it doesn't have a value any more !!!
holidays_to_post = self.holidays_to_post_ids holidays_to_post = self.holidays_to_post_ids
today = fields.Date.context_today(self) today = fields.Date.context_today(self)
if not self.holidays_to_post_ids: # Disable the raise below to make the module "hr_holidays_lunch_voucher"
raise Warning( # work even when nobody took holidays on the current month
_('No leave request to post.')) # if not self.holidays_to_post_ids:
# raise Warning(
# _('No leave request to post.'))
self.holidays_to_post_ids.write({'posted_date': today}) self.holidays_to_post_ids.write({'posted_date': today})
view_id = self.env.ref('hr_holidays_usability.hr_holiday_graph').id view_id = self.env.ref('hr_holidays_usability.hr_holiday_graph').id
action = { action = {

View File

@@ -12,13 +12,13 @@
<field name="name">hr_holidays_post_form</field> <field name="name">hr_holidays_post_form</field>
<field name="model">hr.holidays.post</field> <field name="model">hr.holidays.post</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Post Leaves" version="7.0"> <form string="Post Leaves">
<group name="main" string="Leave Requests to Post"> <group name="main" string="Leave Requests to Post">
<field name="state" invisible="1"/> <field name="state" invisible="1"/>
<field name="before_date" states="draft"/> <field name="before_date" states="draft"/>
<field name="holidays_to_post_ids" nolabel="1" <field name="holidays_to_post_ids" nolabel="1"
context="{'tree_view_ref': 'hr_holidays.view_holiday'}" context="{'tree_view_ref': 'hr_holidays.view_holiday'}"
states="done"/> states="done" colspan="2"/>
</group> </group>
<footer> <footer>
<button name="select_date" type="object" string="Get Holiday Requests" <button name="select_date" type="object" string="Get Holiday Requests"

View File

@@ -23,7 +23,7 @@
{ {
'name': 'Mail Usability', 'name': 'Mail Usability',
'version': '0.1', 'version': '8.0.1.0.0',
'category': 'Base', 'category': 'Base',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'Usability improvements on mails', 'summary': 'Usability improvements on mails',
@@ -37,10 +37,11 @@ Small usability improvements on mails:
* remove 'sent by' in notification footer * remove 'sent by' in notification footer
* add a new entry *All Messages Except Notifications* to the field *Receive Inbox Notifications by Email* of partners (becomes the default value)
""", """,
'author': 'Akretion', 'author': 'Akretion',
'website': 'http://www.akretion.com', 'website': 'http://www.akretion.com',
'depends': ['mail'], 'depends': ['mail'],
'data': [], 'data': ['views/mail.xml'],
'installable': True, 'installable': True,
} }

View File

@@ -20,7 +20,16 @@
# #
############################################################################## ##############################################################################
from openerp import models, api from openerp import models, fields, api
class ResPartner(models.Model):
_inherit = 'res.partner'
notify_email = fields.Selection(
selection_add=[
('all_except_notification', 'All Messages Except Notifications')],
default='all_except_notification')
class MailMail(models.Model): class MailMail(models.Model):
@@ -47,6 +56,19 @@ class MailNotification(models.Model):
footer = super(MailNotification, self).get_signature_footer( footer = super(MailNotification, self).get_signature_footer(
cr, uid, user_id, res_model=res_model, res_id=res_id, cr, uid, user_id, res_model=res_model, res_id=res_id,
context=context, user_signature=user_signature) context=context, user_signature=user_signature)
footer = footer[:footer.find('\n\n<br /><small>Sent by ')] footer = footer[:footer.find('\n<br /><small>Sent by ')]
footer = footer[:footer.find(u'\n\n<br /><small>Envoyé par ')] footer = footer[:footer.find(u'\n<br /><small>Envoyé par ')]
return footer return footer
@api.multi
def get_partners_to_email(self, message):
notify_pids = super(MailNotification, self).get_partners_to_email(
message)
for notif in self:
if (
notif.partner_id.id in notify_pids and
message.type == 'notification' and
notif.partner_id.notify_email ==
'all_except_notification'):
notify_pids.remove(notif.partner_id.id)
return notify_pids

View File

@@ -0,0 +1,92 @@
openerp_FieldMany2ManyTagsEmail = function(instance) {
var _t = instance.web._t;
/**
* Extend of FieldMany2ManyTags widget method.
* When the user add a partner and the partner don't have an email, open a popup to purpose to add an email.
* The user can choose to add an email or cancel and close the popup.
*/
instance.web.form.FieldMany2ManyTagsEmail = instance.web.form.FieldMany2ManyTags.extend({
start: function() {
this.mutex = new openerp.Mutex();
// This widget will indirectly trigger a change:value to it's parent widget
// when setting the value of valid partners. For this reason we have to keep an
// internal state of the last value in order to compute the effective value changes.
this.last_processed_value = [];
this.on("change:value", this, this.on_change_value_check);
// Restore pop-up when trying to send to a partner without email
this.on_change_value_check();
this._super.apply(this, arguments);
},
on_change_value_check : function () {
var self = this;
var values = this.get('value').slice(0); // Clone the array
// We only validate partners emails in case the value is not empty
// and is different from the last processed value
var effective_change = _.difference(values, self.last_processed_value).length;
if (values.length && effective_change) {
this.mutex.exec(function() {
return self._check_email_popup(values);
});
}
},
_check_email_popup: function (ids) {
var self = this;
var valid_partners;
new instance.web.Model('res.partner').call("search", [[
["id", "in", ids],
["email", "=", false],
["notify_email", "in", ['always', 'all_except_notification']]]],
{context: this.build_context()})
.then(function (record_ids) {
var popups_deferreds = [];
self.valid_partners = _.difference(ids, record_ids);
// Propose the user to correct invalid partners
_.each(record_ids, function (id) {
var popup_def = $.Deferred();
popups_deferreds.push(popup_def);
var pop = new instance.web.form.FormOpenPopup(self);
pop.show_element(
'res.partner',
id,
self.build_context(),
{
title: _t("Please complete partner's informations and Email"),
}
);
pop.on('write_completed', self, function () {
self.valid_partners.push(id);
});
pop.on('closed', self, function () {
popup_def.resolve();
});
});
return $.when.apply($, popups_deferreds).then(function() {
// All popups have been processed for the given ids
// It is now time to set the final value with valid partners ids.
var filtered_value = _.uniq(self.valid_partners);
self.last_processed_value = filtered_value;
self.set({'value': filtered_value});
});
});
},
});
/**
* Registry of form fields
*/
instance.web.form.widgets = instance.web.form.widgets.extend({
'many2many_tags_email' : 'instance.web.form.FieldMany2ManyTagsEmail',
});
};

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template id="assets_backend" name="mail usability assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/mail_usability/static/src/js/many2many_tags_email.js"></script>
</xpath>
</template>
</data>
</openerp>

View File

@@ -40,31 +40,27 @@ class ResPartner(models.Model):
def open_aged_open_invoices_report(self): def open_aged_open_invoices_report(self):
aoiwo = self.env['aged.open.invoices.webkit'] aoiwo = self.env['aged.open.invoices.webkit']
afo = self.env['account.fiscalyear'] afo = self.env['account.fiscalyear']
# Force 'date_from' to the first day of the company's life # Filter by period (and not by date) to get
# in order to get the full picture of the partner's account # the report à nouveau
# (we could also do that via a filter by period, but, in this case,
# account_financial_report_webkit doesn't accept "until_date = today" ;
# until_date has to be the end date of the last period
fy_years = afo.search([], order='date_start') fy_years = afo.search([], order='date_start')
date_from = fy_years[0].date_start date_from = fy_years[0].date_start
fy_id = aoiwo._get_fiscalyear() fy_id = aoiwo._get_fiscalyear()
filter_change = aoiwo.onchange_filter( filter_change = aoiwo.onchange_filter(
filter='filter_date', fiscalyear_id=fy_id) filter='filter_period', fiscalyear_id=fy_id)
vals = { vals = {
'fiscalyear_id': fy_id, 'fiscalyear_id': fy_id,
'filter': 'filter_date', 'filter': 'filter_period',
'partner_ids': [(6, 0, [self.commercial_partner_id.id])], 'partner_ids': [(6, 0, [self.commercial_partner_id.id])],
'target_move': 'all', 'target_move': 'all',
} }
vals.update(filter_change['value']) vals.update(filter_change['value'])
vals['date_from'] = date_from
wizard = aoiwo.create(vals) wizard = aoiwo.create(vals)
data = {'form': { data = {'form': {
'chart_account_id': wizard.chart_account_id.id, 'chart_account_id': wizard.chart_account_id.id,
'filter': vals['filter'], 'filter': vals['filter'],
'date_from': vals['date_from'], 'period_from': vals['period_from'],
'date_to': vals['date_to'], 'period_to': vals['period_to'],
'period_to': False, 'date_to': False,
'fiscalyear_id': vals['fiscalyear_id'], 'fiscalyear_id': vals['fiscalyear_id'],
'partner_ids': vals['partner_ids'][0][2], 'partner_ids': vals['partner_ids'][0][2],
'target_move': vals['target_move'], 'target_move': vals['target_move'],

View File

@@ -14,10 +14,20 @@
<field name="phone" string="Phones" <field name="phone" string="Phones"
filter_domain="['|', ('phone', 'like', self), ('mobile', 'like', self)]"/> filter_domain="['|', ('phone', 'like', self), ('mobile', 'like', self)]"/>
</field> </field>
<!-- Display the real field names of the search -->
<!-- We split the search of name, ref / email in two
as searching in email can return a lot of unwanted result
for exemple when you sell on marketplace and search for a customer
amazon you will have all customer that have bought throught the marketplace -->
<field name="name" position="attributes"> <field name="name" position="attributes">
<attribute name="string">Name, Ref, Email</attribute> <attribute name="string">Name, Ref</attribute>
<attribute name="filter_domain">['|', ('display_name','ilike',self), ('ref','=',self)]</attribute>
</field> </field>
<field name="name" position="after">
<field name="email"/>
</field>
</field> </field>
</record> </record>

View File

@@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Copyright 2017 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).
{
"name": "Partner Menu Split",
"summary": "Split the customer menu in two",
"version": "8.0.0.0",
"category": "Uncategorized",
"website": "www.akretion.com",
"author": " Akretion",
"license": "AGPL-3",
"application": False,
"installable": True,
"external_dependencies": {
"python": [],
"bin": [],
},
"depends": [
"base",
"purchase",
],
"data": [
"views/res_partner_view.xml",
],
"demo": [
],
"qweb": [
]
}

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="base.action_partner_customer_form" model="ir.actions.act_window">
<field name="domain">[('parent_id', '=', False)]</field>
<field name="view_mode">tree,form,kanban</field>
</record>
<record id="base.action_partner_form" model="ir.actions.act_window">
<field name="domain">[('parent_id', '=', False)]</field>
<field name="view_mode">tree,form,kanban</field>
</record>
<record id="base.action_partner_form_view1" model="ir.actions.act_window.view">
<field eval="3" name="sequence"/>
</record>
<record id="action_partner_customer_contact_form" model="ir.actions.act_window">
<field name="name">Contact</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,kanban</field>
<field name="domain">[('parent_id', '!=', False)]</field>
<field name="context">{'default_customer':1, 'search_default_customer':1}</field>
<field name="filter" eval="True"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to add a contact in your address book.
</p><p>
Odoo helps you easily track all activities related to
a customer: discussions, history of business opportunities,
documents, etc.
</p>
</field>
</record>
<menuitem
id="menu_partner_form"
parent="base.menu_sales"
action="action_partner_customer_contact_form"
sequence="2"/>
<record id="base.action_partner_supplier_form" model="ir.actions.act_window">
<field name="domain">[('parent_id', '=', False)]</field>
<field name="view_mode">tree,form,kanban</field>
</record>
<record id="action_partner_supplier_contact_form" model="ir.actions.act_window">
<field name="name">Contact</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,kanban</field>
<field name="domain">[('parent_id', '!=', False)]</field>
<field name="context">{'default_supplier':1, 'search_default_supplier':1}</field>
<field name="filter" eval="True"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to add a contact in your address book.
</p><p>
Odoo helps you easily track all activities related to
a supplier: discussions, history of business opportunities,
documents, etc.
</p>
</field>
</record>
<menuitem
id="menu_partner_supplier_form"
parent="purchase.menu_procurement_management"
action="action_partner_supplier_contact_form"
sequence="20"/>
</data>
</openerp>

View File

@@ -0,0 +1,3 @@
# -*- encoding: utf-8 -*-
from . import stock

View File

@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# © 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Product Expiry Simple',
'version': '8.0.1.0.0',
'category': 'Product',
'license': 'AGPL-3',
'summary': 'Simpler and better alternative to the official product_expiry module',
'description': """
Product Expiry Simple
=====================
This module is similar to the official *product_expiry* modules, but much simpler and much better:
* Only one expiry date field instead of 4 !
* date field instead of datetime
* No automatic computing of expiry date based on a delay configured on product (not needed in most companies)
* colored production lot and stock quant tree views depending on expiry dates
* ability to show stats about expiry dates on quants pivot table (thanks to related stored field on stock.quant)
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['stock'],
'conflicts': ['product_expiry'],
'data': [
'stock_view.xml',
'product_removal_data.xml',
],
'installable': True,
}

View File

@@ -0,0 +1,53 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * product_expiry_simple
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-14 13:19+0000\n"
"PO-Revision-Date: 2017-02-14 13:19+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_expiry_simple
#: view:stock.production.lot:product_expiry_simple.search_product_lot_filter
#: view:stock.quant:product_expiry_simple.quant_search_view
msgid "Expired"
msgstr "Périmé"
#. module: product_expiry_simple
#: view:stock.production.lot:product_expiry_simple.search_product_lot_filter
#: field:stock.production.lot,expiry_date:0
#: view:stock.quant:product_expiry_simple.quant_search_view
#: field:stock.quant,expiry_date:0
msgid "Expiry Date"
msgstr "Date de péremption"
#. module: product_expiry_simple
#: model:ir.model,name:product_expiry_simple.model_stock_production_lot
msgid "Lot/Serial"
msgstr "Lot/N° série"
#. module: product_expiry_simple
#: view:stock.production.lot:product_expiry_simple.search_product_lot_filter
#: view:stock.quant:product_expiry_simple.quant_search_view
msgid "Not Expired"
msgstr "Non périmé"
#. module: product_expiry_simple
#: model:ir.model,name:product_expiry_simple.model_stock_quant
msgid "Quants"
msgstr "Quants"
#. module: product_expiry_simple
#: view:stock.production.lot:product_expiry_simple.view_production_lot_tree
#: view:stock.quant:product_expiry_simple.view_stock_quant_tree
msgid "red:expiry_date and expiry_date < current_date;green:expiry_date >= current_date"
msgstr "red:expiry_date and expiry_date < current_date;green:expiry_date >= current_date"

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" ?>
<openerp>
<data>
<record id="removal_fefo" model="product.removal">
<field name="name">First Expiry First Out (FEFO)</field>
<field name="method">fefo</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,43 @@
# -*- 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 openerp import models, fields, api
class StockProductionLot(models.Model):
_inherit = 'stock.production.lot'
_rec_name = 'display_name'
expiry_date = fields.Date(string='Expiry Date')
display_name = fields.Char(
compute='compute_display_name_field',
string='Lot/Serial Number Display', store=True, readonly=True)
@api.multi
@api.depends('name', 'expiry_date')
def compute_display_name_field(self):
for lot in self:
dname = lot.name
if lot.expiry_date:
dname = '[%s] %s' % (lot.expiry_date, dname)
lot.display_name = dname
class StockQuant(models.Model):
_inherit = 'stock.quant'
expiry_date = fields.Date(
related='lot_id.expiry_date', store=True, readonly=True)
# method copy/pasted from the official product_expiry module
# © Odoo SA
@api.model
def apply_removal_strategy(
self, location, product, qty, domain, removal_strategy):
if removal_strategy == 'fefo':
order = 'expiry_date, location_id, package_id, lot_id, in_date, id'
return self._quants_get_order(
location, product, qty, domain, order)
return super(StockQuant, self).apply_removal_strategy(
location, product, qty, domain, removal_strategy)

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<openerp>
<data>
<!-- LOTS -->
<record id="view_production_lot_form" model="ir.ui.view">
<field name="name">product_expiry_simple.stock.production.lot.form</field>
<field name="model">stock.production.lot</field>
<field name="inherit_id" ref="stock.view_production_lot_form"/>
<field name="arch" type="xml">
<field name="ref" position="after">
<field name="expiry_date"/>
</field>
</field>
</record>
<record id="view_production_lot_tree" model="ir.ui.view">
<field name="name">product_expiry_simple.stock.production.lot.tree</field>
<field name="model">stock.production.lot</field>
<field name="inherit_id" ref="stock.view_production_lot_tree"/>
<field name="arch" type="xml">
<field name="product_id" position="after">
<field name="expiry_date"/>
</field>
<xpath expr="/tree" position="attributes">
<attribute name="colors">red:expiry_date and expiry_date &lt; current_date;green:expiry_date &gt;= current_date</attribute>
</xpath>
</field>
</record>
<record id="search_product_lot_filter" model="ir.ui.view">
<field name="name">product_expiry_simple.stock.production.lot.search</field>
<field name="model">stock.production.lot</field>
<field name="inherit_id" ref="stock.search_product_lot_filter"/>
<field name="arch" type="xml">
<field name="product_id" position="after">
<filter string="Expired" name="expired"
domain="[('expiry_date', '&lt;', context_today().strftime('%Y-%m-%d'))]"/>
<filter string="Not Expired" name="no-expired"
domain="[('expiry_date', '&gt;=', context_today().strftime('%Y-%m-%d'))]"/>
</field>
<group expand="0" position="inside">
<filter name="expiry_date_groupby" string="Expiry Date"
context="{'group_by': 'expiry_date:month'}"/>
</group>
</field>
</record>
<!-- QUANTS -->
<!-- No need to inherit the form view, because the expiry date is shown in the lot_id via display_name field -->
<record id="view_stock_quant_tree" model="ir.ui.view">
<field name="name">product_expiry_simple.stock.quant.tree</field>
<field name="model">stock.quant</field>
<field name="inherit_id" ref="stock.view_stock_quant_tree"/>
<field name="arch" type="xml">
<field name="lot_id" position="after">
<field name="expiry_date"/>
</field>
<xpath expr="/tree" position="attributes">
<attribute name="colors">red:expiry_date and expiry_date &lt; current_date;green:expiry_date &gt;= current_date</attribute>
</xpath>
</field>
</record>
<record id="quant_search_view" model="ir.ui.view">
<field name="name">product_expiry_simple.stock.quant.search</field>
<field name="model">stock.quant</field>
<field name="inherit_id" ref="stock.quant_search_view"/>
<field name="arch" type="xml">
<filter name="internal_loc" position="after">
<separator/>
<filter string="Expired" name="expired"
domain="[('expiry_date', '&lt;', context_today().strftime('%Y-%m-%d'))]"
groups="stock.group_production_lot"/>
<filter string="Not Expired" name="no-expired"
domain="[('expiry_date', '&gt;=', context_today().strftime('%Y-%m-%d'))]"
groups="stock.group_production_lot"/>
</filter>
<filter context="{'group_by' : 'lot_id'}" position="after">
<filter name="expiry_date_groupby" string="Expiry Date"
context="{'group_by': 'expiry_date:month'}" groups="stock.group_production_lot"/>
</filter>
</field>
</record>
</data>
</openerp>

View File

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

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Copyright 2017 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).
{
"name": "Product Simple Accounting",
"summary": "Simplify the accounting on product, remove accounting tab",
"version": "8.0.1.0.0",
"category": "Uncategorized",
"website": "www.akretion.com",
"author": " Akretion",
"license": "AGPL-3",
"application": False,
"installable": True,
"external_dependencies": {
"python": [],
"bin": [],
},
"depends": [
"product_profile",
"account_product_fiscal_classification",
],
"data": [
"views/product_view.xml",
],
"demo": [
],
"qweb": [
]
}

View File

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

View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright 2017 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 ProductProfile(models.Model):
_inherit = 'product.profile'
def _get_types(self):
return [('product', 'Stockable Product'),
('consu', 'Consumable'),
('service', 'Service')]
property_account_income = fields.Many2one(
'account.account',
'Property Account Income',
company_dependent=True)
property_account_expense = fields.Many2one(
'account.account',
'Property Account Expense',
company_dependent=True)

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="product_template_view_form" model="ir.ui.view">
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="priority" eval="64"/>
<field name="arch" type="xml">
<page string="Accounting" position="replace"/>
<field name="profile_id" position="after">
<field name="categ_id"/>
<field name="fiscal_classification_id" widget="selection"/>
</field>
<field name="standard_price" position="after">
<field name="valuation" attrs="{'invisible':[('type', 'in', ('service', 'consu'))]}"/>
</field>
</field>
</record>
</data>
</openerp>

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,23 @@
# -*- 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': '8.0.1.0.0',
'category': 'Product',
'website': 'http://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,base.group_sale_manager,1,0,0,0
sale.access_product_uom_categ_sale_manager,product.uom.categ salemanager,product.model_product_uom_categ,base.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 base.group_sale_manager 1 0 0 0
7 sale.access_product_uom_categ_sale_manager product.uom.categ salemanager product.model_product_uom_categ base.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"?>
<openerp>
<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</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>
</openerp>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="product.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="product.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="product.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_unit_measure_purchase" 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>
</openerp>

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Product Usability module for Odoo # Product Usability module for Odoo
@@ -29,3 +29,20 @@ class ProductTemplate(models.Model):
price_history_ids = fields.One2many( price_history_ids = fields.One2many(
'product.price.history', 'product_template_id', 'product.price.history', 'product_template_id',
string='Product Price History') string='Product Price History')
name = fields.Char(track_visibility='onchange')
type = fields.Selection(track_visibility='onchange')
categ_id = fields.Many2one(track_visibility='onchange')
list_price = fields.Float(track_visibility='onchange')
sale_ok = fields.Boolean(track_visibility='onchange')
active = fields.Boolean(track_visibility='onchange')
state = fields.Selection(track_visibility='onchange')
weight = fields.Float(track_visibility='onchange')
weight_net = fields.Float(track_visibility='onchange')
class ProductProduct(models.Model):
_inherit = 'product.product'
default_code = fields.Char(track_visibility='onchange')
ean13 = fields.Char(track_visibility='onchange')
active = fields.Boolean(track_visibility='onchange')

View File

@@ -85,9 +85,6 @@
<field name="name" position="attributes"> <field name="name" position="attributes">
<attribute name="class"></attribute> <attribute name="class"></attribute>
</field> </field>
<field name="description" position="attributes">
<attribute name="attrs">{'invisible': True}</attribute>
</field>
<!-- END for wider 'name' field --> <!-- END for wider 'name' field -->
</field> </field>
</record> </record>
@@ -128,6 +125,78 @@
</field> </field>
</record> </record>
<record id="product_ul_form_view" model="ir.ui.view">
<field name="name">usability.product.ul.form</field>
<field name="model">product.ul</field>
<field name="inherit_id" ref="product.product_ul_form_view"/>
<field name="arch" type="xml">
<!-- Useusally, the size of packaging is written in cm,
so I propose to use that UoM for size of packaging.
Nothing is written in the native product module about this -->
<field name="height" position="replace">
<label for="height"/>
<div name="height">
<field name="height" class="oe_inline"/>
<label string=" cm" class="oe_inline"/>
</div>
</field>
<field name="width" position="replace">
<label for="width"/>
<div name="width">
<field name="width" class="oe_inline"/>
<label string=" cm" class="oe_inline"/>
</div>
</field>
<field name="length" position="replace">
<label for="length"/>
<div name="length">
<field name="length" class="oe_inline"/>
<label string=" cm" class="oe_inline"/>
</div>
</field>
<field name="weight" position="replace">
<label for="weight"/>
<div name="weight">
<field name="weight" class="oe_inline"/>
<label string=" kg" class="oe_inline"/>
</div>
</field>
</field>
</record>
<record id="product_ul_tree" model="ir.ui.view">
<field name="name">usability.product.ul.tree</field>
<field name="model">product.ul</field>
<field name="inherit_id" ref="product.product_ul_tree"/>
<field name="arch" type="xml">
<field name="type" position="after">
<!-- not so important, but we have room in the tree view, so let's use it -->
<field name="height"/>
<field name="width"/>
<field name="length"/>
<field name="weight"/>
</field>
</field>
</record>
<!-- Add missing search view on product.ul -->
<!-- It doesn't work: when you activate the group by type, it gives a JS error
TypeError: key.indexOf is not a function
-->
<!--
<record id="product_ul_search" model="ir.ui.view">
<field name="name">usability.product.ul.search</field>
<field name="model">product.ul</field>
<field name="arch" type="xml">
<search string="Search Logistic Units">
<field name="name"/>
<group string="Group By" name="groupby">
<filter name="type_groupby" string="Type" context="{'group_by': 'type'}"/>
</group>
</search>
</field>
</record>
-->
</data> </data>
</openerp> </openerp>

View File

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

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Copyright 2017 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).
{
"name": "Product Variant Usability",
"summary": "Improve the product variant usability",
"version": "8.0.1.0.0",
"category": "Uncategorized",
"website": "www.akretion.com",
"author": " Akretion",
"license": "AGPL-3",
"application": False,
"installable": True,
"external_dependencies": {
"python": [],
"bin": [],
},
"depends": [
"attribute_usability",
"product_variant_inactive",
],
"data": [
"views/product_view.xml",
],
"demo": [
],
"qweb": [
]
}

View File

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

View File

@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Copyright 2017 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 ProductTemplate(models.Model):
_inherit = 'product.template'
all_product_variant_ids = fields.One2many(
'product.product',
'product_tmpl_id',
'All Product Variant',
domain=['|', ('active', '=', False), ('active', '=', True)])

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="product_template_view_form" model="ir.ui.view">
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_only_form_view"/>
<field name="priority" eval="64"/>
<field name="arch" type="xml">
<field name="attribute_line_ids" position="after">
<field name="all_product_variant_ids" nolabel="1" required="0">
<tree create="0">
<field name="default_code"/>
<field name="attribute_value_ids" widget="many2many_tags"/>
<field name="qty_available"/>
<field name="active" invisible="1"/>
<button type="object"
name="button_activate"
attrs="{'invisible': [('active','=',True)]}"
icon="gtk-stop"/>
<button type="object"
name="button_deactivate"
attrs="{'invisible': [('active','=',False)]}"
icon="gtk-apply"/>
</tree>
</field>
</field>
<xpath expr="//field[@name='product_variant_count']/../.." position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@@ -277,12 +277,16 @@ class PurchaseSuggestPoCreate(models.TransientModel):
('warehouse_id.company_id', '=', company.id)] ('warehouse_id.company_id', '=', company.id)]
pick_types = spto.search( pick_types = spto.search(
pick_type_dom + [( pick_type_dom + [(
'default_location_dest_id', 'default_location_dest_id', 'child_of', location.id)])
'child_of',
location.location_id.id)])
# I use location.parent_id.id to support 2 step-receptions # I use location.parent_id.id to support 2 step-receptions
# where the stock.location .type is linked to Warehouse > Receipt # where the stock.location .type is linked to Warehouse > Receipt
# but location is Warehouse > Stock # but location is Warehouse > Stock
if not pick_types:
pick_types = spto.search(
pick_type_dom + [(
'default_location_dest_id',
'child_of',
location.location_id.id)])
if not pick_types: if not pick_types:
pick_types = spto.search(pick_type_dom) pick_types = spto.search(pick_type_dom)
if not pick_types: if not pick_types:

View File

@@ -26,6 +26,7 @@ for any help or question about this module.
'depends': ['purchase'], 'depends': ['purchase'],
'data': [ 'data': [
'purchase_view.xml', 'purchase_view.xml',
'stock_view.xml',
'supplierinfo_view.xml', 'supplierinfo_view.xml',
], ],
'active': False, 'active': False,

View File

@@ -70,7 +70,7 @@ msgstr "Partenaire"
#. module: purchase_usability_extension #. module: purchase_usability_extension
#: view:product.supplierinfo:purchase_usability_extension.product_supplierinfo_view_filter #: view:product.supplierinfo:purchase_usability_extension.product_supplierinfo_view_filter
msgid "Product" msgid "Product"
msgstr "Article :" msgstr "Article"
#. module: purchase_usability_extension #. module: purchase_usability_extension
#: view:product.supplierinfo:purchase_usability_extension.product_supplierinfo_view_filter #: view:product.supplierinfo:purchase_usability_extension.product_supplierinfo_view_filter

View File

@@ -1,6 +1,6 @@
# coding: utf-8 # -*- coding: utf-8 -*-
# Copyright (C) 2015 Akretion (http://www.akretion.com) # © 2015-2017 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com> # @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api from openerp import models, fields, api
@@ -30,6 +30,15 @@ class PurchaseOrder(models.Model):
partner_ref = fields.Char(track_visibility='onchange') partner_ref = fields.Char(track_visibility='onchange')
class StockPicking(models.Model):
_inherit = 'stock.picking'
# Field added to have a clickable link from picking to PO
purchase_id = fields.Many2one(
related='move_lines.purchase_line_id.order_id', readonly=True,
string='Purchase Order')
class ResPartner(models.Model): class ResPartner(models.Model):
_inherit = 'res.partner' _inherit = 'res.partner'
@@ -55,3 +64,9 @@ class ResPartner(models.Model):
compute='_purchase_stats', string='# of Purchase Order') compute='_purchase_stats', string='# of Purchase Order')
supplier_invoice_count = fields.Integer( supplier_invoice_count = fields.Integer(
compute='_purchase_stats', string='# Supplier Invoices') compute='_purchase_stats', string='# Supplier Invoices')
class ProductTemplate(models.Model):
_inherit = 'product.template'
purchase_ok = fields.Boolean(track_visibility='onchange')

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2017 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).
-->
<openerp>
<data>
<record id="view_picking_form" model="ir.ui.view">
<field name="name">purchase_usability.stock.picking.form</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<field name="origin" position="after">
<field name="purchase_id" attrs="{'invisible': [('picking_type_code', '!=', 'incoming')]}"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -1,4 +1,4 @@
# coding: utf-8 # -*- coding: utf-8 -*-
# © 2015 David BEAL @ Akretion # © 2015 David BEAL @ Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
@@ -8,5 +8,8 @@ from openerp import models, fields
class ProductSupplierinfo(models.Model): class ProductSupplierinfo(models.Model):
_inherit = 'product.supplierinfo' _inherit = 'product.supplierinfo'
supplier_is_company = fields.Boolean( # Change product_tmpl_id to required=False
comodel_name='res.partner', related='name.is_company') # because I added that field to form view and it blocks when you save
# the product form with a new supplier info
# This field is now required in the form view
product_tmpl_id = fields.Many2one(required=False)

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