Skip to content

[16.0][ADD] advance_payment: advance payment agreement module#591

Open
n3n wants to merge 60 commits into16.0from
16.0-add-advance_payment-v2
Open

[16.0][ADD] advance_payment: advance payment agreement module#591
n3n wants to merge 60 commits into16.0from
16.0-add-advance_payment-v2

Conversation

@n3n
Copy link
Copy Markdown
Member

@n3n n3n commented Mar 13, 2026

Summary

Add complete advance payment agreement lifecycle management module (สัญญายืมเงิน) for employee loan tracking. Includes 4 core models with state workflow (draft → in_progress → in_review → done), 2 wizards for usage records and return confirmations, master data for loan types and banks, and Thai Buddhist year sequence.

Implementation

  • 4 models: agreement, usage line, loan type, and bank
  • Dual wizards for guided usage recording and return workflows
  • State machine with READONLY_STATES pattern and sequence generation
  • Security rules with master data admin-only management
  • Full menu hierarchy with configuration section

🤖 Generated with Claude Code

@coreichamp coreichamp force-pushed the 16.0-add-advance_payment-v2 branch from 93c4bc2 to e00fbc8 Compare March 17, 2026 11:34
@n3n n3n force-pushed the 16.0-add-advance_payment-v2 branch 4 times, most recently from 881a4bc to 1050af8 Compare April 3, 2026 13:59
n3n and others added 25 commits April 17, 2026 16:38
…ญญายืมเงิน)

Implement complete advance payment lifecycle management with loan agreement tracking,
usage records, and return workflows. Includes master data for loan types and banks,
Thai Buddhist year sequence, and full state machine workflow.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…kflow

- Rename model advance.payment.agreement → advance.payment
- New state flow: draft → submitted → approved → in_progress → done
- Replace borrower_name/department/etc. with requested_by (res.users) and department_id (hr.department)
- Add Reference field linking to purchase.request
- Add advance.payment.method model (pattern: purchase.guarantee.method)
- Auto-create outbound account.payment on approve
- Auto-transition to in_progress when linked payment is posted
- Add payment stat button on form
- Add account_payment_kmitl, hr, purchase_request dependencies
- Bump version to 16.0.2.0.0
- Batch _compute_method_id: single search instead of N+1
- Batch action_approve: create payments in one call, write state once
- Add action_start() method to encapsulate in_progress transition
- account_payment.action_post: delegate to action_start() and batch write
…ent field

- Remove advance.payment.method model (not needed)
- Remove reference_model and method_id fields from advance.payment
- Hide advance_payment_id on account.payment form when empty
…cle events

Post informative internal notes at each workflow step:
- action_submit: submitted by, loan amount, and reason
- action_approve: payment link, amount, partner, journal
- action_start: payment link, disbursed amount, partner
- action_close: amount used vs. remaining
…ation

Add exception rules to validate loan amount must be > 100 and <= 50,000 baht
using the base_exception framework with configurable blocking rules.
Use xpath to add related_model_id before exception_ids instead of
trying to modify a non-existent field in the parent view.
Move exception_rule_views.xml after advance_payment_menus.xml so the
parent menu_advance_payment_config exists when referenced.
- Wizard: check blocking exceptions before allowing ignore (match
  purchase_exception pattern), reset ignore_exception when blocked
- Remove dead onchange_ignore_exception (loan_amount is readonly in
  submitted state)
- Remove action_ignore_exceptions button from form (both rules are
  blocking so it always errors)
Add 4 accounting dimension fields (store=False) computed from
analytic_distribution with inverse functions:
- department_analytic_id (ส่วนงาน)
- source_analytic_id (แหล่งเงิน)
- fund_analytic_id (กองทุน)
- activity_analytic_id (ด้าน/แผนงาน/กิจกรรม)

Depends on account_analytic_kmitl for plan code support.
…bution to payment

- Rename _ANALYTIC_PLAN_TO_FIELD to _analytic_keys (matches analytic.mixin convention)
- Replace custom _inverse_analytic_id with _update_analytic_distribution from analytic.mixin
- Pass analytic_distribution to account.payment in _prepare_account_payment_vals
…base_exception UI

Set view_id to base_exception.view_exception_rule_confirm in the action,
following the pattern used by purchase_exception, disbursement, kmitl_project.
Without view_id, Odoo cannot find a primary form view for the wizard model
so the exception dialog renders blank.

Remove the redundant inherited view — related_model_id is populated via
default_get context and does not need to appear in the view.
Call payments.action_post() immediately after creating the payment so
the advance.payment transitions directly to in_progress without
requiring manual confirmation of the payment.

The existing hook in account_payment.action_post handles the
state transition and chatter via action_start().
… for payment

action_submit is the KMITL workflow step defined in account_payment_kmitl,
not action_post. Move the advance_payment hook accordingly.
…status

- Add disbursement_state field (pending/paid) on advance.payment
- Set to 'pending' when payment is created on approval
- Set to 'paid' when account.payment is posted
- Show in tree view as optional badge after state column
- Add module category and two security groups:
  - User: can create/submit advance payments, sees own records
  - Manager: can approve, sees all records, inherits user
- Add record rules: users restricted to own records (requested_by)
- Restrict approve button to manager group
- Restrict root menu to advance_payment_user group
- Update access CSV to use module-specific groups
- Remove unlink permission for users on usage lines
- Fix 1: simplify department_id default lambda (unnecessary conditional)
- Fix 2: add copy=False to usage_line_ids and payment_ids
- Fix 3: avoid duplicate chatter on exception re-submission by writing
  state directly instead of calling action_submit() in exception wizard
- QW1: override write() to block protected field changes on non-draft records
- QW2: validate submitter in action_submit() (requestor or manager only)
- QW3: add date_submitted, date_approved, date_closed tracking fields
- QW4: show disbursement_state in form view (hidden when False), tree optional=hide
- QW5: enforce agreement number uniqueness via @api.constrains (skips 'New')
…yment redesign

F1 - Cancel/Reject Workflow:
- Add 'cancel' state to agreement lifecycle
- Add cancel_reason field (readonly, copy=False)
- Override write() already blocks protected fields on cancel state
- action_open_reject_wizard(): manager sends submitted agreement back to draft with reason
- action_open_cancel_wizard(): manager cancels submitted/approved/in_progress agreement
- _action_do_cancel(): voids linked payments (draft/submitted → cancel, posted → draft → cancel)
- _action_do_reject(): submitted → draft with reason stored in cancel_reason
- New advance.payment.cancel.wizard model with reason input
- Manager-only buttons in form header: ส่งกลับแก้ไข (reject) and ยกเลิกสัญญา (cancel)
- 'cancel' badge decoration-danger in list view
- 'Cancelled' filter in search view

F2 - Return Payment Flow Redesign:
- Add is_return_requested Boolean field on agreement
- Return wizard now requires proof attachment (Many2many ir.attachment)
- Wizard confirms return request only (no auto-close): sets is_return_requested=True,
  relinks attachments to agreement, posts chatter message
- 'แจ้งคืนเงิน' button: visible for in_progress + NOT is_return_requested
- 'ปิดสัญญา' button (manager only): visible for in_progress + is_return_requested
- Manager manually creates inbound payment then closes the agreement
n3n added 29 commits April 17, 2026 16:38
Close workflow:
- action_close() shows confirmation wizard when amount_remaining > 0,
  with warning about outstanding balance; closes directly when remaining=0
- New wizard: advance.payment.close.confirm

State protections when done/cancel:
- Return lines tab: readonly (no create/edit/delete)
- Attachments tab: readonly (prevents deletion of existing attachments)
- Chatter still allows adding new attachments (default Odoo behavior)

Reopen closed agreements:
- New action_reopen(): done → in_progress, clears date_closed
- Visible only to base.group_system (ERP admin) with confirm dialog

Alert banner fix:
- Hide disbursement_state banners when state is done or cancel

Tests: 36 cases (+5 new: wizard confirm, direct close, reopen, guards)
Translate all 199 entries including field labels, state selections,
validation messages, chatter log messages, and wizard UI strings.
…yment

Part A — advance_payment fixes:
- Remove required=True from bank_id (now optional at creation)
- Add exception rules: missing bank account, missing department,
  missing analytic dimensions (all 4 required before submit)

Part B — purchase_request_kmitl:
- Rename payment_type 'loan' → 'advance' (value + label)
- Update view warning message for advance payment type

Part C — New module purchase_request_advance_payment:
- Extend purchase.request with advance_payment_id (1:1 link)
- "สร้างสัญญาเงินยืม" button on approved PRs with payment_type=advance
  Creates draft advance.payment with data from PR:
  requested_by, department_id, loan_amount (estimated_cost),
  loan_type=procurement, loan_reason, analytic_distribution, reference
- Smart button "สัญญาเงินยืม" to navigate to linked AP
- Cross-post: disbursement message posted to PR chatter when AP is paid
Follow-up to payment_type rename in purchase_request_kmitl.
Demo purchase requests used the old 'loan' value which no longer exists.
…_type on AP from PR

When an advance payment is created from a purchase request, the reference
and loan_type_id fields are now readonly on the AP form to prevent
accidental changes. Fields remain editable when creating AP standalone
(reference is empty).
- Add row-level decoration colors matching state badge colors
- Add create_date column (optional=show)
- Move main_exception_id to optional=show (was always visible)
- Add reference and loan_reason as optional=hide columns
- Code formatting: self-closing XML tags, consistent indentation
… PR fields

- Extract _prepare_advance_payment_vals() for override support
- Use self.department_id.id directly instead of traversing employee
- Lock additional fields on AP form when created from PR:
  loan_amount, requested_by, department_id (readonly when reference is set)
New setting in Settings → สัญญายืมเงิน:
- Strict Submit Mode (advance_payment.strict_submit)
- When ON: only the requestor or ERP admin can submit
- When OFF (default): requestor, manager, or admin can submit

Refactor action_submit() submitter check into _check_submit_permission()
for clarity and to read the config parameter.
- Add @api.constrains: loan_amount cannot be negative (< 0)
- Update exception rule: loan_amount must be > 0 to submit
  (changed from previous threshold of 100)
New group: group_advance_payment_officer
- Hierarchy: User → Officer → Manager
- Officer sees all records (same record rule as Manager)
- Read-only access on all models (no create/write/delete)
- Intended for finance staff who need to monitor agreements
  without modifying them
New module adding operating unit filtering to advance payments.

Models:
- advance.payment: operating_unit_id field (editable in draft, readonly after)
- advance.payment.usage.line: related from agreement
- advance.payment.return.line: related from agreement

Security:
- Global ir.rules filtering by user's allowed operating units
  for advance.payment, usage lines, and return lines

Views:
- Form: operating_unit_id before requested_by
- Tree: operating_unit_id column (optional=hide)
- Search: group by operating unit filter
- All views restricted to group_multi_operating_unit
…lacement

New module: advance_payment_operating_unit_access_all
- Group: Access all OUs' advance payment
- Overrides ir.rules for advance.payment, usage lines, and return lines
  to allow full cross-OU access for users in the group

Fix advance_payment_operating_unit views (diff comments):
- Form: move operating_unit_id after department_id (was before requested_by)
- Tree: use xpath //tree/inside instead of field position
Add config parameter 'advance_payment.allow_manual_reference':
- When enabled: users can specify Reference field manually on create
- When disabled (default): Reference field is readonly, can only be
  set by the system (e.g. when creating from a purchase request)
ir.config_parameter.get_param returns string "True"/"False", not boolean.
Compare with == "True" to get the correct boolean value.
Use base_setup.action_general_configuration instead of
base.action_general_configuration (not available in CE).
Add base_setup to depends.
Two issues:
1. Bridge module's attrs on reference field completely replaced the base
   view's attrs, so allow_manual_reference was never checked. Fixed by
   merging both conditions: readonly when setting disabled OR reference
   already has a value (from PR).
2. Simplified config param read to explicit string comparison
   (param == "True") instead of str2bool which had edge cases with
   boolean default values.
…le method

Replace view-level attrs override with a Python method pattern:
- Base module: _is_reference_readonly() checks config setting
- Bridge module: overrides to also lock when reference has a value
- View uses computed is_reference_readonly field for attrs

This avoids bridge module attrs completely replacing base attrs,
and makes the readonly logic extensible for future modules.
Fill all remaining empty msgstr entries including new fields:
officer group, strict submit mode, allow manual reference,
is_reference_readonly, loan amount constraint, and Thai UI strings.
15 test cases covering the full PR → AP bridge flow:
- Create AP from approved PR (fields populated, analytic distribution)
- Validation guards (state, payment_type, 1:1 constraint)
- Cross-post on disbursement (action_start posts to PR chatter)
- No cross-post for standalone AP without reference
- is_reference_readonly override (from PR vs standalone)
- Config setting allow_manual_reference integration
- Smart button action returns correct AP form action
…le field locking

Add computed field is_locked_by_reference with overridable method
_is_locked_by_reference() in the base module. Fields loan_type_id,
loan_amount, requested_by, and department_id use this field in attrs
to become readonly when locked.

Base module always returns False (no locking). Bridge module overrides
to return True when reference has a value (created from PR).

This removes all attrs overrides from the bridge module view, following
the same pattern as is_reference_readonly for the reference field.
…tralize field locking

Loan Type Model:
- Add reference_model (Char) field: empty=standalone, value=reference-required
- Set reference_model="purchase.request" on loan_type_procurement
- Standalone types (travel, education, etc.) have no reference_model

Advance Payment Model:
- Replace is_reference_readonly with two computed fields:
  is_reference_visible (show/hide reference field based on loan_type + setting)
  is_locked_by_reference (lock all data fields when reference has a value)
- Add _prepare_vals_from_reference() hook for bridge modules to auto-fill
- Add @api.onchange('reference') to call auto-fill on selection
- Add @api.onchange('loan_type_id') to clear reference when switching to standalone
- Domain on loan_type_id filters to standalone types by default
- Reference field: invisible when not needed, readonly after save (id != False)

View:
- All lockable fields (requested_by, department_id, loan_type_id, loan_amount,
  loan_reason) use is_locked_by_reference for readonly attrs in base view
- No attrs overrides needed in bridge modules

Bridge Module (purchase_request_advance_payment):
- Remove _is_reference_readonly override (no longer exists)
- Add _prepare_vals_from_reference override for PR-specific auto-fill
- Remove all view attrs overrides (empty view file)

DX for future bridge modules:
1. Create loan_type with reference_model = "your.model"
2. Override _prepare_vals_from_reference() to return auto-fill vals
3. Provide action to create AP — no view changes needed
…om_reference override

Simplify bridge module — the override added complexity without clear
benefit since AP is already created with all values populated via
_prepare_advance_payment_vals() in purchase_request.py.
@madara1150 madara1150 force-pushed the 16.0-add-advance_payment-v2 branch from 42e3e6f to 61b9c73 Compare April 17, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant