[19.0][IMP] contract UX ❤️#1427
Conversation
There were an error in previous query for moving only contracts with the mark checked, but it's also more logic to move them, but remain them disabled.
Currently translated at 22.2% (47 of 212 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/hr/
…emplate Fix this use-case: If the contract journal is not set on the contract template the contract is created without a journal when confirming the sale order
… with duplicated name - Don't execute onchange after invoice creation Using that approach (that is the current one in core) has a lot of side effects and performance bottlenecks. You can read odoo/odoo#40156 for summarizing them. This also improves the handling of the values of payment term an fiscal position for using the partner ones if not set. - Tests with duplicated name So they are not executed at all. Detected by chance looking for a test for the other PR.
- rename misnamed methods - clarify _get_recurring_next_date First compute the next period end date, then derive the next invoice date from the next period stard and end date. - handle max_date_end in _get_recurring_next_date This concentrates all next date calculation logic in one place, and will allow further simplifications. - add next period start/end fields Add two computed field showing the next period start and end date. This improve the UX and will enable further simplifications in the code. - refactor _get_period_to_invoice Move the part of the logic that compute the next period depending on the chosen next invoice date to _get_next_period_date_end.
Currently translated at 40.1% (85 of 212 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/fr/
- REF: Refactor _update_recurring_next_date Reuse the logic that is now fully located in _get_recurring_next_date. - REF: re-add _compute_first_recurring_next_date for backward compatibility - FIX: add missing dependency in computed field - REF: remove one monthlylastday special case get_relative_delta now works the same for all recurring rules. Move the special case handling to _init_last_date_invoiced which is used only for migration. - IMP: support pre-paid for monthlylastday monthlylastday is (almost) not a special case anymore \o/. montlylastday is simply a montly period where the periods are aligned on month boundaries. The last bit of special casing is that postpaid generates invoice the day after the last dasy of the period, except for monthlylastday where the invoice is generated on the last day of the period. This last exception will disappear when we put the offset under user control. This is a breaking change because the post-paid/pre-paid mode becomes relevant for monthlylastday invoicing. The field becomes visible in the UI. Code that generate monthlylastday contract lines must now correctly set the pre-paid/post-paid mode too. Some tests have had to be adapted to reflect that. - REF: make recurring_invoicing_offset a computed field In preparation to making it user modifiable. - REF: make get_next_period_date_end public Make it public because it is the core logic of the module. Also, clarify that recurring_invoicing_type and recurring_invoicing_offset are needed only when we want the next period to be computed from a user chosen next invoice date. - REF: rename _get_recurring_next_date as get_next_invoice_date It is easier to understand. Also make it public.
…combinations for next invoicing period + simplify _get_period_to_invoice
Currently translated at 100.0% (216 of 216 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/pt_BR/
- Add failing test for next invoice date before the last date invoiced - raise an error when next invoice date before the last date invoiced - Add note field to contract - add new option: create_new_line_at_contract_line_renew Add a company config option to decide whether to create or to extend contract line at renew action - extend contract line at renewal - improve code: unify methods argument _renew_create_line and _renew_extend_line
Currently translated at 91.9% (204 of 222 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/it/
…ontract line stop + stop update recurring_next_date
If you have contracts in several companies, cron will create all of them, but property fields will be populated with incorrect data as the taken company is the main from the cron user (usually admin).
Currently translated at 38.1% (85 of 223 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/fr/
Currently translated at 0.9% (2 of 223 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/es_CL/
Currently translated at 99.6% (222 of 223 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/es/
Currently translated at 38.6% (86 of 223 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/fr/
I have detected a method that was created as redundant and with the same technique used when preparing the line values, so better to have everything together in the same method instead of having it spread.
It happen that a company has to trigger the invoicing action to generate invoices before the scheduled date (to print and prepare invoices documents, check invoices, etc.). This requires technical access for end users with the risk that this represents. This commit adds a new wizard to run the invoicing action for a given date with a helper to see and check the contract that will be invoiced. When the manual action is called, the system displays all created invoices. [12.0][IMP] - log the manual invoice action in contract chatter [IMP] - Add alink to the invoice in contract message at manual invoicing [IMP] - Improve code [FIX] - log message for invoice creation only when there is an invoice [IMP] - split the manual invoice menu into to menus sale & purhcase [IMP] - hide invoice button if there is nothing to invoice
Currently translated at 91.0% (213 of 234 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/it/
Currently translated at 96.6% (226 of 234 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/pt_BR/
Currently translated at 100.0% (234 of 234 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/fi/
Translated using Weblate (Portuguese) Currently translated at 99.6% (233 of 234 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/pt/
Currently translated at 100.0% (234 of 234 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/fi/
…lter + contract termination
Currently translated at 100.0% (254 of 254 strings) Translation: contract-12.0/contract-12.0-contract Translate-URL: https://translation.odoo-community.org/projects/contract-12-0/contract-12-0-contract/fr/
The field is declared on contract.recurring.mixin and inherited by both contract.line and contract.contract, but only the line variant is ever written (in _update_last_date_invoiced, called from _prepare_recurring_invoices_values on contract_lines). On contract.contract the field is therefore always empty even after invoices are generated, which also silently breaks _compute_next_period_date_start / _compute_next_period_date_end on the contract record (they always fall back to date_start). Override the field on contract.contract as a stored compute aggregating the max of contract_line_ids.last_date_invoiced, ignoring cancelled lines and pure section/note rows. Mirrors the existing patterns for recurring_next_date (min) and date_end (max). Also expose the field as an optional column in the contract list view and add a "Last Invoice" group-by filter in the search view, mirroring the existing "Next Invoice" entries.
List view: - Decorate finished contracts (date_end < today) muted, upcoming contracts (date_start > today) in blue. - Show responsible with many2one_avatar_user, date_end as remaining_days, recurring_next_date as visible columns; date_start, journal_id and last_date_invoiced as opt-in optional columns. - Add an activities column using the standard list_activity widget. Form view: - Render the responsible (user_id) with many2one_avatar_user. Search view: - Add a "My Contracts" filter and a user_id search field. - Add Responsible and Journal group-bys. - Drop the duplicate empty <separator/> and frame the In-progress / Finished and Archived filters between separators, matching the invoice search-view pattern.
The contract module had no kanban view defined, so users got Odoo's default fallback rendering with only the contract name. Add a real kanban view modelled after the invoice kanban: partner name (bold) and tags on top, contract name in the middle, code and remaining days to the next invoice in muted text, activity icon and responsible avatar in the footer. The card menu carries a color picker plus Open, Duplicate, Archive / Restore and Delete actions. The card itself uses highlight_color="color" so the user-chosen color renders as a left sidebar, mirroring project tasks. Add a color integer field on contract.contract to back the color picker. Switch both customer and supplier action view_modes to list,kanban,activity,form so the new kanban view becomes available and the auto-generated activity view (from mail.activity.mixin) is reachable from the breadcrumb.
The search view exposes both partner_id ("Associated Partner") and
commercial_partner_id ("Commercial Entity") as group-bys, but the
distinction is opaque to users. Rename to "Contact" and "Company": the
partner_id stores the actual contact selected (which may be a child
contact at a company), while commercial_partner_id resolves the parent
commercial entity used for cross-contract roll-ups. Same wording works
for both sale and purchase contract types.
product_id on contract.template.line had no domain, so archived products remained selectable from the contract form. The customer and supplier line views also overrode the field domain to filter by sale_ok/purchase_ok, replacing any inherited active filter. Add a default domain on the field excluding archived products, and extend the customer and supplier domains to keep filtering by active in addition to sale_ok / purchase_ok.
Replace the old generic OCA icon with a contract-specific icon, and ship the SVG source alongside the PNG following OCA practice. This fixes the broken icon on the community appstore listing and gives the module a recognisable badge in activity headers, the apps list and the breadcrumb. Drop the unused legacy contract_icon.svg under static/src/img and point the portal "Show Contracts" entry at the new SVG so the portal home and the rest of Odoo show the same icon.
Form view (contract.contract): - Promote the contract name to <h1> for visual parity with sale orders / quotations. - Render partner_id with res_partner_many2one for the chip-style display with avatar. - Move Responsible (user_id) and Company out of the main header into a "Sales" group on the Other Information notebook page, where they belong with the rest of the back-office metadata. Form view (contract.template): - Same <h1> name treatment for the template form. - Show the standard Archived ribbon when active is False. List view (contract.contract): - Add contract_template_id as an optional column. Search view (contract.contract): - Add an "Upcoming" filter for contracts whose date_start is in the future. Tighten "In progress" so a future-dated contract no longer matches both filters. Search view (contract.template): - Add an Archived filter, mirroring the contract search view, now that templates can be archived. Note field is converted from Text to sanitised Html so users can format their contract notes the same way they can on a sale order.
…tract The contract.line model already has a date_start <= date_end constraint (_check_start_end_dates in contract_line.py), but no equivalent existed on contract.contract itself, so setting date_end before date_start on a contract with line_recurrence=False (or directly on the contract record) silently succeeded. Add an api.constrains check on contract.contract that mirrors the line-level constraint.
Filename:
- Add print_report_name to the report action so the downloaded file
is "Contract - {name}.pdf" instead of the generic "report.pdf".
Layout (mimicking the sale order / quotation report):
- Drop the "Partner:" caption from the address block; the chosen
contact widget already labels itself.
- Render the contract name as a large h2 heading at the top of the
page.
- Add an "informations" bubble row carrying Reference, Date Start,
Date End, Responsible and Payment Terms instead of cramming them
into a single column.
- Promote the "Recurring Items", "Modifications" and "Notes" headings
to h4 to match the visual hierarchy.
- Render the (now Html) note via t-field so saved formatting is
preserved, and skip the section entirely when the note is empty.
Bring the portal contract page closer to the sale-order portal layout: - Replace the small <h5> contract title with an <h2 class="quote_header_1"> matching the SO portal heading style. - Add a sidebar with Download and Print buttons so portal users can easily get a copy of the contract PDF. - Surface Payment Terms in the general information block; previously they were not shown.
Ship demo records that exercise the various contract states and the
recurring-line description markers (#START#, #END#, #INVOICEMONTHNAME#),
so a fresh demo install immediately illustrates how the module behaves:
- Three tags (Premium, Renewal Required, Internal) showcasing the
many2many tags widget with colours.
- A "Monthly Subscription Template" demonstrating how to set up a
contract template with a section, a recurring product line using
the description markers and a note line.
- Three contracts:
- Demo Running Contract: started 6 months ago, runs another 6,
monthly post-paid, tagged Premium.
- Demo Expired Contract: ran for a year and ended last month,
tagged Renewal Required.
- Demo Upcoming Contract: starts next month, monthly pre-paid,
instantiated from the template, multi-tagged.
…re flag Make the recurring-invoice cron resilient to bad data on a single contract without giving up batch performance. Strategy: * Fast path - one batched ``account.move.create`` call per company, identical to the upstream behaviour and performance. * On failure - fall back to per-contract processing inside savepoints, so one bad contract does not block its company batch and SQL-level errors do not poison the transaction for the remaining contracts. Failure visibility: * New stored fields on contract.contract: - invoice_generation_error (Text) - invoice_generation_error_date (Datetime) - has_invoice_generation_error (Boolean, computed/stored) * Search filter "Invoice Generation Failed" on the contract list. * Orange warning triangle button in the list view that opens the form for inspection; row is also tinted with decoration-warning. * Warning banner on the form sheet showing the error and a Dismiss button. * On failure: chatter post + TODO activity assigned to the contract's responsible user; both de-duplicated so consecutive failures do not spam. * On the next successful run: error fields are cleared and the activity is auto-resolved. Tests cover: * Single bad contract does not block healthy ones in the same company * Error fields and activity are populated on failure * Successful retry clears the flag and resolves the activity * No duplicate chatter / activity on consecutive failures * Healthy batches still hit the single-call fast path * Manual Dismiss button clears the flag without running the cron
* Drop unused ``contracts`` local in ``test_cron_isolates_failing_contract`` (ruff F841) * Apply ruff-format pass over the new test cases and the form-view banner indentation * Use ``_logger.error(..., exc_info=exc)`` instead of ``_logger.exception`` in ``_record_invoice_generation_error``. The helper is also called outside an active ``except`` block (e.g. when tests seed the failure flag directly), which made ``_logger.exception`` log a useless ``NoneType: None`` line. ``exc_info=exc`` logs the traceback when one is attached to the exception and a clean line otherwise.
Robust recurring-invoice cron — client contextPushed two commits ( The problemWe hit this on a multi-company production deployment with a few hundred active recurring contracts. One contract had a bad data entry that made The painful part for the client wasn't just the missed invoicing run, it was finding the bad apple. There's no per-contract error trail. The cron log shows one stack trace (without naming the offending contract clearly) and there's no in-UI marker. Their bookkeeper had to scan hundreds of contracts manually to figure out which one was tripping the cron. What this PR adds
Why per-contract savepoints (not just a per-contract
|
The CI ``checklog-odoo`` step fails the build on any ERROR line in the test log. ``test_cron_clears_error_on_recovery`` and ``test_action_clear_invoice_generation_error`` deliberately call ``_record_invoice_generation_error`` to seed a flagged contract, which emits a legitimate ERROR log line. Wrap those seeding calls in ``mute_logger`` so the test path stays silent. Tests that exercise the cron itself already use ``mute_logger``; this just covers the two helpers that seed the flag directly.
Odoo core PR #248401 introduced a new JS widget (product_label_section_and_note_field) that depends on the translated_product_name field. Since contract.line uses this widget but does not define the field, opening a contract form crashes with KeyError: 'translated_product_name'. Add a computed Text field that mirrors the pattern used by sale.order.line in Odoo core: it returns the product display_name translated into the contract partner's language.
This PR introduces schema changes (color, last_date_invoiced compute, the three invoice_generation_error fields), view replacements (form header / list view / search filter), and new constraints. Deploy runners that gate odoo -u on a manifest version change would otherwise leave the database on the previous schema and crash on first access with errors like: psycopg2.errors.UndefinedColumn: column contract_contract.color does not exist Bump the minor version so the next deploy auto-runs the module update.
Without an explicit string=, Odoo auto-derives the column header
from the field name. On stored compute fields that redefine a
mixin-level field, the auto-derived label can be confusing or even
collide with another field's label depending on locale and the field
order in which the registry is built. Production users saw the
contract list-view column for last_date_invoiced rendered as
'Last Updated On' (the default label of write_date).
Set string="Date of Last Invoice" on the field on both
contract.recurring.mixin and the contract.contract override so
the label is deterministic across the form, list, search, and
group-by views. Mirrors the style of the sibling
recurring_next_date field ("Date of Next Invoice").
Bump manifest 19.0.1.1.0 -> 19.0.1.1.1 so deploy runners pick up the
new label.
The redesigned portal contract page (commit bbf9fa8) used ``data-snippet="s_timeline"`` and ``data-snippet="s_card"`` attributes on the modification timeline. These attributes are how the website module discovers snippets to wire JS Interactions to. On a portal page (no website builder) the Interaction's ``setup()`` runs but the DOM children it expects to find via ``this.el.querySelector(...)`` are absent, so it crashes with: TypeError: can't access property 'dataset', this.el.querySelector(...) is null Drop the ``data-snippet`` attributes. The visual classes (``s_timeline``, ``s_timeline_card``, etc.) stay so the layout is unchanged. The Interaction is no longer wired up, so it can't fail. Manifest 19.0.1.1.1 -> 19.0.1.1.2.
Bring the contract tag administration in line with Odoo's other tag models (res.partner.category, crm.tag, …): - Drop the separate form view and switch the list to editable="bottom", so administrators manage tags inline without a popup. - Render the color column with widget="color_picker", which makes it obvious which colour the tag uses and lets users change it in place. - Pick a random colour (1-11) by default when a tag is created, so new tags don't all collapse onto colour 0 in the kanban / many2many_tags widgets. - Drop the form view_mode on the action since the form view no longer exists.
The ContractContract.group_id field is a Many2one to account.analytic.account, computed from the lines' analytic distribution. The historical 'Group' label dates from a much older version where contracts were grouped by analytic account, and the field name still carries the legacy concept. To anyone reading a contract today the label is opaque: 'Group of what?' Rename the label to 'Analytic Account', matching what every other place in Odoo calls the same model. Add a help string so the relationship to the line-level analytic distribution is documented. No schema or behaviour change -- pure UX cleanup. Manifest 19.0.1.1.2 -> 19.0.1.1.3 so opaas re-runs -u and the new label lands in the database.
The "Other Information" tab on the contract form had two outer ``<group>`` blocks. The first wrapped a single inner "Sales" group (rendered on the left half, with the right half empty); the second was a bare top-level ``<group>`` containing ``code``, ``group_id`` and ``currency_id`` -- which Odoo's form layout renders as a single full-width column, so each of those fields was visibly wider than the Sales group fields above and broke the visual rhythm of the tab. Merge them into one outer ``<group>`` with two inner groups (``Sales`` + ``Administration``), so the whole tab renders as a consistent 2-column grid and the second column lines up next to the first. No model change, no functional change -- pure layout fix.


Based upon #1312
To merged after that one.
Some bugfixes and UX polishments