Commit Graph
21 Commits
Author SHA1 Message Date
Goffin Simon c11179b0b4 [FIX] purchase: Smart button in contact view form
Steps to reproduce the bug:

- Let's consider a company contact C with two children CH1 and CH2
- Create a purchase order for CH1 and a subscription for CH2
- Archive CH1 and CH2

Bug:

The count of purchase orders in C was displaying 0 instead of 2

Same issue for the vendor bills

opw:2360155

closes odoo/odoo#61167

X-original-commit: b3e31f2680534382b048324cfcb8a6f6c457553f
Signed-off-by: Simon Goffin (sig) <sig@openerp.com>
2020-11-02 14:04:26 +00:00
Fabien Pinckaers ccfc54113d [IMP] purchase: Improve reminder mail usability
Some change to imporve reminder mail usability:

1. Scheduled date on POL of the PO form now is always editable.
2. Improve tooltip for the reminder email
3. don't show number of days when reminder email not checked on
   both PO and partner form.
4. only show "confirm receipt date" button in debug model
5. In the mail, format date by partner.lang
6. Add button to send preview reminder mail
7. On default, receipt_reminder_email on res.partner is False
8. merge expected_date with date_planned (done by FP)

Task 2265912
PR #52693
2020-06-09 13:59:10 +00:00
yhu-odoo 181c7d82e3 [IMP] purchase: send reminder mail to vendor
1. automatically send a reminder mail to vendor to confirm the receipt
date. If confirmed, (confirmed by vendor) will be added next to the
receipt date. If not, vendor can update the date on the portal website.
An warning activity will be set for the purchase representative for this
update.

2. Vendor can also comfirm recieption of the PO when we 'send PO by mail'.
If confirm, (confirmed by vendor) will be added next to the confirmation
date. An filter is added in the PO search view to show all unconfirmed
PO.

Task 2230811
PR #49921

Signed-off-by: Simon Lejeune (sle) <sle@openerp.com>
2020-05-27 08:20:17 +00:00
Ankita Raval d675dbaa4c [IMP] account,* : Change type field to move_type in account.move
task-id: 2028z813
2020-02-19 09:09:20 +00:00
Lucas Lefèvre 46e48055ed [IMP] account: Store customer/supplier rank on partners
Purpose
=======

To correctly choose in which account a line should be posted,
we have to know if the partner is a customer or a supplier.

Specification
=============

Keep track of the number of account moves "in" and "out"
a partner has. These counts should be based on the posted
account moves. A customer that has been created from the
'Customer' menuitem will have a rank=1. When a customer
invoice will be created for him, the generated account moves
will be taken into account to compute its rank. The most
invoices we have for a partner, the higher his rank is.

Note: To avoid any concurrent update failures on the partner,
if one transaction has already locked a partner row, the count
update will be skipped that time.

This means the values may be approximative in the database!
The exact values will eventually be correctly computed at
the next successfull try.

Known limitation of this approach: The computation ignores
the set of currently selected companies. Actually, storing
context dependent values in the database is a bad practice,
and is avoided in that case by taking all the companies into
account.

Use the stored fields `customer_rank` and `supplier_rank`
to order partners when searching by name. This allows to show
best customers or best suppliers on top.

To choose if best customer or supplier are shown on top,
the context key `res_partner_search_mode` is used.
The context key can take two values: 'customer' or 'supplier'.
This decision partially reverts/revamps 8766f38 to only use
account moves instead of PO and SO

On actions showing partners, set a default filters to menus to
only display customers (customer_rank > 0) if the string is
"Customers", and only suppliers if the string is "Vendors" or
"Suppliers".

TaskID: 2049131

closes odoo/odoo#35942

Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
2019-08-22 09:29:26 +00:00
Raphael Collet 9920f20e4c [IMP] models: ORM speedup
This branch is the combination of several optimizations in the ORM:

* store field values once in the cache: the cache reflects more
faithfully the database, only fields that explicitly depend on the
context have an extra indirection in the cache;

* delay recomputations by default: use method `recompute` to explicitly
flush out pending recomputations;

* delay updates in method `write`: updates are stored in a data
structure that can be flushed efficiently to the database with method
`flush` (which also flush out recomputations);

* make method `modified` take advantage of inverse fields to inverse
dependencies;

* filter records by evaluating a domain on records in Python;

* a computed field with `readonly=False` behaves like a normal field
with an onchange method;

* computed fields are computed in superuser mode by default.

Work done by Toufik Ben Jaa, Raphael Collet, Denis Ledoux and Fabien
Pinckaers.

closes odoo/odoo#35659

Signed-off-by: Denis Ledoux <beledouxdenis@users.noreply.github.com>
2019-08-20 12:43:59 +00:00
Lucas LefèvreandYannick Tivisse 8766f388da [IMP] base: Allow to better search partners if they are customers/suppliers
This commit allows to call `name_search` with context key
`res_partner_search_mode` that can take currently two values: "customer" or
 "supplier".

When ordering search results, order partners by the number of SO they have when
the value is "customer" and by the number of PO if the value is "supplier".

Top suppliers/customers are displayed above in a PO/SO partner dropdown.

We have thought about different implementations before selecting this one:

- Keep the 2 boolean flags and automatically set them if a SO or PO is created
  for a customer/supplier (helps a bit but doesn't work well for the first
  SO/PO). As the onboarding is also a priority, we didn't implement it.

- Instead of filtering on the bool flags, sort the name_search() result according
  to the number of orders they already have (e.g. if you search for "Foo" on a
  PO, a supplier "SuperFoo" with 32 purchase orders will appear before supplier
  "HorribleFoo" that has no PO). Problem is to do this in an efficient manner for
  large databases - possibly we could store a "relevance index" based on the
  number
  of recent orders. Storing those values is an issue too, as it doesn't work well
  for multi company database. Indeed, storing values that depend on the context
  is certainly not a good idea.

- Same as previous alternative but with no stored columns, rather a JOIN in
  name_search. This is the solution that have been kept. The only concern was
  about the perfomances. We tested it on our production base, on a sales order,
  as we have way much more customers than suppliers. After a deep analysis on
  the querry and the bitmap generated by PostgreSQL, we can conclude that the
  count(*) works rather efficiently and that the request is not significantly
  heavier than without the context key.

Performance Analysis
====================

The following results have been obtained on our production database.

Before this commit
------------------

The original query is the following one:

```
EXPLAIN ANALYSE
SELECT res_partner.id
   FROM "res_partner"
 WHERE ("res_partner"."customer" = True) AND ("res_partner"."active" = True) AND ((("res_partner"."type" != 'private') OR "res_partner"."type" IS NULL)  OR  "res_partner"."type" IS NULL ) AND  (res_partner.email ilike '%eezee-i%'
     OR res_partner.display_name ilike '%eezee-i%'
     OR res_partner.ref ilike '%eezee-i%'
     OR res_partner.vat ilike '%eezeei%')
     -- don't panic, trust postgres bitmap
ORDER BY res_partner.display_name ilike '%eezee-i%' desc,
        res_partner.display_name
limit 8;
```

Here is the EXPLAIN ANALYSE returned by PostgreSQL:

```
 Limit  (cost=1625.50..1625.52 rows=8 width=21) (actual time=13.397..13.404 rows=8 loops=1)
   ->  Sort  (cost=1625.50..1626.32 rows=326 width=21) (actual time=13.395..13.396 rows=8 loops=1)
         Sort Key: (((display_name)::text ~~* '%eezee-i%'::text)) DESC, display_name
         Sort Method: top-N heapsort  Memory: 26kB
         ->  Bitmap Heap Scan on res_partner  (cost=1282.79..1618.98 rows=326 width=21) (actual time=13.025..13.350 rows=56 loops=1)
               Recheck Cond: (((email)::text ~~* '%eezee-i%'::text) OR ((display_name)::text ~~* '%eezee-i%'::text) OR ((ref)::text ~~* '%eezee-i%'::text) OR ((vat)::text ~~* '%eezeei%'::text))
               Rows Removed by Index Recheck: 1
               Filter: (customer AND active AND (((type)::text <> 'private'::text) OR (type IS NULL) OR (type IS NULL)))
               Rows Removed by Filter: 1
               Heap Blocks: exact=58
               ->  BitmapOr  (cost=1282.79..1282.79 rows=328 width=0) (actual time=12.983..12.983 rows=0 loops=1)
                     ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..322.22 rows=162 width=0) (actual time=5.022..5.022 rows=54 loops=1)
                           Index Cond: ((email)::text ~~* '%eezee-i%'::text)
                     ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..322.22 rows=162 width=0) (actual time=4.128..4.128 rows=56 loops=1)
                           Index Cond: ((display_name)::text ~~* '%eezee-i%'::text)
                     ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..321.00 rows=1 width=0) (actual time=2.240..2.240 rows=0 loops=1)
                           Index Cond: ((ref)::text ~~* '%eezee-i%'::text)
                     ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..317.03 rows=4 width=0) (actual time=1.585..1.585 rows=0 loops=1)
                           Index Cond: ((vat)::text ~~* '%eezeei%'::text)
 Planning time: 0.766 ms
 Execution time: 13.489 ms
```

After this commit
-----------------

Without the context key, the query looks like this:

```
SELECT res_partner.id
   FROM "res_partner"
 WHERE ("res_partner"."active" = True) AND ((("res_partner"."type" != 'private') OR "res_partner"."type" IS NULL)  OR  "res_partner"."type" IS NULL ) AND  (res_partner.email ilike '%eezee-i%'
     OR res_partner.display_name ilike '%eezee-i%'
     OR res_partner.ref ilike '%eezee-i%'
     OR res_partner.vat ilike '%eezeei%')
     -- don't panic, trust postgres bitmap
GROUP BY res_partner.id
ORDER BY COUNT(*) DESC, res_partner.display_name ilike '%eezee-i%' desc,
        res_partner.display_name
limit 8;

And it quite clear when looking at the EXPLAIN ANALYSE that the request
is quite the same, except the aggregation that is made with a quicksort method.

 Limit  (cost=1645.00..1645.02 rows=8 width=29) (actual time=13.200..13.206 rows=8 loops=1)
   ->  Sort  (cost=1645.00..1645.82 rows=328 width=29) (actual time=13.197..13.198 rows=8 loops=1)
         Sort Key: (count(*)) DESC, (((display_name)::text ~~* '%eezee-i%'::text)) DESC, display_name
         Sort Method: top-N heapsort  Memory: 26kB
         ->  GroupAggregate  (cost=1631.88..1638.44 rows=328 width=29) (actual time=13.090..13.160 rows=56 loops=1)
               Group Key: id
               ->  Sort  (cost=1631.88..1632.70 rows=328 width=20) (actual time=13.080..13.084 rows=56 loops=1)
                     Sort Key: id
                     Sort Method: quicksort  Memory: 29kB
                     ->  Bitmap Heap Scan on res_partner  (cost=1282.79..1618.17 rows=328 width=20) (actual time=12.793..13.055 rows=56 loops=1)
                           Recheck Cond: (((email)::text ~~* '%eezee-i%'::text) OR ((display_name)::text ~~* '%eezee-i%'::text) OR ((ref)::text ~~* '%eezee-i%'::text) OR ((vat)::text ~~* '%eezeei%'::text))
                           Rows Removed by Index Recheck: 1
                           Filter: (active AND (((type)::text <> 'private'::text) OR (type IS NULL) OR (type IS NULL)))
                           Rows Removed by Filter: 1
                           Heap Blocks: exact=58
                           ->  BitmapOr  (cost=1282.79..1282.79 rows=328 width=0) (actual time=12.755..12.755 rows=0 loops=1)
                                 ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..322.22 rows=162 width=0) (actual time=4.823..4.823 rows=54 loops=1)
                                       Index Cond: ((email)::text ~~* '%eezee-i%'::text)
                                 ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..322.22 rows=162 width=0) (actual time=3.941..3.941 rows=56 loops=1)
                                       Index Cond: ((display_name)::text ~~* '%eezee-i%'::text)
                                 ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..321.00 rows=1 width=0) (actual time=2.186..2.186 rows=0 loops=1)
                                       Index Cond: ((ref)::text ~~* '%eezee-i%'::text)
                                 ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..317.03 rows=4 width=0) (actual time=1.798..1.798 rows=0 loops=1)
                                       Index Cond: ((vat)::text ~~* '%eezeei%'::text)
 Planning time: 0.761 ms
 Execution time: 13.317 ms
```

With the context key, the query looks like this:

```
EXPLAIN ANALYSE
SELECT res_partner.id
   FROM "res_partner"
   LEFT JOIN sale_order ON res_partner.id = sale_order.partner_id
 WHERE ("res_partner"."active" = True) AND ((("res_partner"."type" != 'private') OR "res_partner"."type" IS NULL)  OR  "res_partner"."type" IS NULL ) AND  (res_partner.email ilike '%eezee-i%'
     OR res_partner.display_name ilike '%eezee-i%'
     OR res_partner.ref ilike '%eezee-i%'
     OR res_partner.vat ilike '%eezeei%')
     -- don't panic, trust postgres bitmap
GROUP BY res_partner.id
ORDER BY COUNT(*) DESC, res_partner.display_name ilike '%eezee-i%' desc,
        res_partner.display_name
limit 8;
```

The only difference is that a nested loop is made for the left join, as postgreSQL has
correctly identified the dicriminating table. The correct result is obtained within
a similar duration.

```
 Limit  (cost=2700.68..2700.70 rows=8 width=29) (actual time=12.561..12.568 rows=8 loops=1)
   ->  Sort  (cost=2700.68..2701.50 rows=328 width=29) (actual time=12.559..12.560 rows=8 loops=1)
         Sort Key: (count(*)) DESC, (((res_partner.display_name)::text ~~* '%eezee-i%'::text)) DESC, res_partner.display_name
         Sort Method: top-N heapsort  Memory: 26kB
         ->  GroupAggregate  (cost=2687.56..2694.12 rows=328 width=29) (actual time=12.449..12.527 rows=56 loops=1)
               Group Key: res_partner.id
               ->  Sort  (cost=2687.56..2688.38 rows=328 width=20) (actual time=12.408..12.425 rows=305 loops=1)
                     Sort Key: res_partner.id
                     Sort Method: quicksort  Memory: 42kB
                     ->  Nested Loop Left Join  (cost=1283.21..2673.86 rows=328 width=20) (actual time=11.629..12.340 rows=305 loops=1)
                           ->  Bitmap Heap Scan on res_partner  (cost=1282.79..1618.17 rows=328 width=20) (actual time=11.596..11.862 rows=56 loops=1)
                                 Recheck Cond: (((email)::text ~~* '%eezee-i%'::text) OR ((display_name)::text ~~* '%eezee-i%'::text) OR ((ref)::text ~~* '%eezee-i%'::text) OR ((vat)::text ~~* '%eezeei%'::text))
                                 Rows Removed by Index Recheck: 1
                                 Filter: (active AND (((type)::text <> 'private'::text) OR (type IS NULL) OR (type IS NULL)))
                                 Rows Removed by Filter: 1
                                 Heap Blocks: exact=58
                                 ->  BitmapOr  (cost=1282.79..1282.79 rows=328 width=0) (actual time=11.560..11.560 rows=0 loops=1)
                                       ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..322.22 rows=162 width=0) (actual time=4.724..4.724 rows=54 loops=1)
                                             Index Cond: ((email)::text ~~* '%eezee-i%'::text)
                                       ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..322.22 rows=162 width=0) (actual time=3.825..3.825 rows=56 loops=1)
                                             Index Cond: ((display_name)::text ~~* '%eezee-i%'::text)
                                       ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..321.00 rows=1 width=0) (actual time=1.752..1.752 rows=0 loops=1)
                                             Index Cond: ((ref)::text ~~* '%eezee-i%'::text)
                                       ->  Bitmap Index Scan on res_partner_name_tgm_idx_gin  (cost=0.00..317.03 rows=4 width=0) (actual time=1.254..1.254 rows=0 loops=1)
                                             Index Cond: ((vat)::text ~~* '%eezeei%'::text)
                           ->  Index Only Scan using sale_order_partner_id_index on sale_order  (cost=0.42..2.74 rows=48 width=4) (actual time=0.006..0.007 rows=5 loops=56)
                                 Index Cond: (partner_id = res_partner.id)
                                 Heap Fetches: 12
 Planning time: 1.056 ms
 Execution time: 13.791 ms
```

TaskID: 2031147

Co-authored-by: Yannick Tivisse <yti@odoo.com>
2019-08-01 12:42:03 +02:00
Adrian Torres 4b38cc6590 [REM] *: calls to @api.multi
Multi is the default api for methods, it is not necessary to explicitly
decorate methods with it, adds clutter and most people use it because
they see that the rest of the code uses it.

Done with `find . -type f -name '*.py' | xargs sed -i '/@api.multi/d'`
2019-07-17 14:13:12 +02:00
Laurent Smet bc131c0cfb [MERGE] manual forward port of accounting-pocalypse (beaa30a3d1)
This commit merges the following models
 * account.invoice and account.move
 * account.invoice.line and account.move.line
 * account.voucher and account.move
 * account.voucher.line and account.move.line

It was the opportunity for a big cleanup of the code, so it also restructures the whole account module, its different models/fields, the tests etc. for a better world and a better code readability.

==== Rationale ====
The rationale of this huge change is that we want journal entries / invoices to be easily edited, and changes reflected in the other model. It's a HUGE feature and very strategic for the fiduciary companies. For example, changing the account of a journal entry needs to be automatically reflected on the related invoice.

The same reasoning applies to sale/purchase vouchers.

==== Changes made in features =====
When creating an invoice, you are now creating a journal entry directly.
--> The object account.invoice no longer exists.
In the same fashion when creating an invoice line, you're now adding journal items directly in the journal entry representing the invoice. If this invoice line has some tax, it may create additional journal items as well.
--> The models account.invoice.line & account.invoice.tax no longer exist

Identically, when creating a sale/purchase receipt with its lines, you are now creating a journal entry directly and there's no more usability difference between encoding a receipt or an invoice.
--> The object account.voucher no longer exists.
--> The object account.voucher.line no longer exists.
--> The whole account_voucher module no longer exists.

Positive side-effects coming from these changes are
* draft invoices/bills/sale or purchase receipts now create a draft accounting entry. Validate these objects now simply post its journal entry. That means that draft invoices/bills/sale or purchase receipt can straightforwardly be included in reporting or budgets.
* opening a journal entry in form view will now always open the correct view: if it's a sale/purchase journal entry we will have a customer invoice/vendor bill view or a sale/purchase receipt view, whatever the menu we're coming from.
* code & business logic simplification. It is also condensed in a single place instead of being partially duplicated on invoices, vouchers and journal entries.

There should be no feature loss, except the one allowing to group multiple journal items together based on the same product during the invoice validation.

==== Changes made in models =====
* account.invoice: model removed. Instead, now use account.move with following mapping

field (account.invoice) 		field (account.move)
-----------------------			--------------------
name 					invoice_payment_ref
number 					name
reference 				ref
comment 				narration
user_id 				invoice_user_id
amount_					total_company_signed amount_total_signed
residual 				amount_residual
state 					state + invoice_payment_state 		/!\ selection changed
date_invoice 				invoice_date
date_due 				invoice_date_due
sent 					invoice_sent
origin 					invoice_origin
payment_term_id 			invoice_payment_term_id
partner_bank_id 			invoice_partner_bank_id
incoterm_id 				invoice_incoterm_id
vendor_bill_id 				invoice_vendor_bill_id
source_email 				invoice_source_email
vendor_display_name 			invoice_vendor_display_name
invoice_icon 				invoice_vendor_icon
cash_rounding_id 			invoice_cash_rounding_id
sequence_number_next 			invoice_sequence_number_next
sequence_number_next_prefix 		invoice_sequence_number_next_prefix

'invoices' subset of account.move can be accessed by using the selection field 'type' or one of the many helpers like is_invoice()

* account.move: now has a valid state 'cancel' that has to be excluded from all business logic
* account.move: field 'amount' renamed into 'amount_total'
* account.move: field 'reverse_entry_id' renamed into 'reversed_entry_id'
* account.move.line: now has a field 'display_type' that has to be excluded from all business logic, in order to support invoice layouting
* account.invoice.line: model removed. Instead, now use account.move.line with following mapping

field (account.invoice.line) 		field (account.move.line)
----------------------------		-------------------------
invoice_id 				move_id
uom_id 					product_uom_id
invoice_line_tax_ids 			tax_ids
account_analytic_id 			analytic_account_id

'invoice lines' subset of all account.move.line from a journal entry can be accessed by using the boolean field 'exclude_from_invoice_tab'

* account.invoice.tax: model removed. Instead, now use account.move.line with following mapping

field (account.invoice.tax) 		field (account.move.line)
---------------------------		-------------------------
invoice_id 				move_id
account_analytic_id 			analytic_account_id
amount 					price_unit
base 					tax_base_amount

'tax lines' subset of all account.move.line from a journal entry can be accessed by using the relational field 'tax_line_id'

* account.invoice.confirm: model removed. Instead, now use the 'post()' function of account.move
* account.invoice.refund: model removed. Instead, now use account.move.reversal to reverse the entries with the same options as we had for invoices
* account.voucher: model removed. Instead, now use account.move of type in ['out_receipt', 'in_receipt]
* account.voucher.line: model removed. Instead, now use account.move.line

==== Changes made in functions ====
* on account.move, method _run_post_draft_to_post() renamed into _autopost_draft_entries()
* on account.move, method action_account_invoice_payment() renamed into action_invoice_register_payment()
* on account.move, method action_invoice_reconcile_to_check() renamed into action_open_matching_suspense_moves()
* on account.move, method _get_domain_edition_mode_available() renamed into _get_domain_matching_supsense_moves()
* on account.move, method _get_intrastat_country_id() renamed into _get_invoice_intrastat_country_id()
* on account.move.line, method _get_domain_for_edition_mode() renamed into _get_suspense_moves_domain()
* in account.bank.statement, contextual key 'edition_mode' renamed into 'suspense_moves_mode'

Was task 1917430
2019-07-01 13:45:57 +02:00
Christophe Simonis 7276e8203e [FIX] purchase: correct previous forward-port.
Domain was apply on the wrong model.
2019-01-18 14:37:51 +01:00
Christophe Simonis a337b9ec92 [MERGE] forward port branch 12.0 up to f854e01a98 2019-01-18 14:26:33 +01:00
Christophe Simonis 378b283c02 [MERGE] forward port branch saas-11.3 up to 83cc046e9a 2019-01-16 17:02:34 +01:00
Christophe Simonis 952f784454 [MERGE] forward port branch 11.0 up to 4f2f299534 2019-01-15 17:48:36 +01:00
Denis Vermylen e1767490e9 [FIX] purchase: loading time on partners with many children
When loading a partner form view, the ORM prefetches the computed fields
purchase_order_count and supplier_invoice_count for all the related
partners as well (parent, children, ..).

Instead of reading the orders and invoices once per partner, we group
them all together.

opw 1919933

closes odoo/odoo#29788
2019-01-02 16:08:55 +00:00
Cas Vissers 43edf08b26 [FIX] purchase: include refunds in invoice count
If a supplier had a refund, it was not included in the supplier invoice count
nor in the results in the statbutton

On the customer side, fields like total_invoiced works on both the out_invoice
and out_refund
2018-10-25 14:41:14 +00:00
Thomas Binsfeld f9644a7674 [IMP] purchase: split compute methods
No advantage to group a search on purchase.order and another one on
account.invoice.
Avoid potential access errors if a user has access to one model but not the
other one.

Closes #25761
2018-08-16 17:34:23 +02:00
Thibault Delavallée 1d544087fa [FIX][IMP] purchase: set purchase_warn as non required
When reinitializing modules it is impossible to create partners if
purchase is installed because purchase_warn field has a required
column but no default value if the module does not depend from
purchase. Use case is trying to run test_mail tests with purchase
installed.

As this field is not business critical it is now not required. As there
is a default value behavior should not change for users.

See also 71181a0e39, dd47abdcc3 and 06a02ae4c2.
2018-01-08 09:42:10 +01:00
Fabien Pinckaers b341b5ca73 [IMP] Remove duplicate fields names 2018-01-07 19:28:03 +01:00
Thibault Delavallée c129b91b6d [MOV] base: move res_* models into models/ 2017-11-27 11:15:00 +01:00
Denis Vermylen (dve) ba3d3582bc [MIG] purchase: Migrate to new API 2016-08-05 14:04:37 +02:00
Denis Vermylen (dve) 05b265de9a [MOV] purchase: move and rename files according to guidelines 2016-08-05 12:52:49 +02:00