Skip to content

Refactoring Immutabilité + PartnerInventory#4

Open
fleuronvilik wants to merge 19 commits intomainfrom
utils
Open

Refactoring Immutabilité + PartnerInventory#4
fleuronvilik wants to merge 19 commits intomainfrom
utils

Conversation

@fleuronvilik
Copy link
Copy Markdown
Owner

@fleuronvilik fleuronvilik commented Mar 31, 2026

  • Understand the issue: pi is None in void_sales_report silently returns early instead of raising an error
  • Create infra/errors.py with DataIntegrityError (with docstring)
  • Update app/use_cases.py void_sales_report to raise DataIntegrityError when inventory lines are missing, including partner_id and book_ids in the error message
  • All 122 tests pass
  • Code review addressed (error message specificity, docstring)
  • No CodeQL security alerts

- add id to SalesReport, return a new instance on void, and reload persisted reports

- align repository mapping, helpers, and tests with the immutable sales report model
- switch List, Dict, Set, Tuple, and Optional usages to built-in generic types

- align domain, policies, use cases, and test helpers with the newer annotations
- return new inventory instances from deliver, sale, and restore operations

- update use cases and add inventory behavior tests for sales and voids
- rename the delivery request repository method and update use case call sites

- align test repo helpers and SQL repo tests with the new method name
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors core domain entities to be immutable and introduces a persisted PartnerInventory model, shifting “stock” from being projected from deliveries/reports to being stored and updated transactionally during delivery and sales report workflows.

Changes:

  • Make DeliveryRequest / SalesReport immutable (@dataclass(frozen=True)) and update transitions to return new instances.
  • Add partner_inventories table + SqlPartnerInventoryRepo, and wire it into Context/bootstrap/tests.
  • Update delivery + sales report use-cases to mutate partner inventory on mark_delivered, submit_sales_report, and void_sales_report.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/test_use_cases.py Updates tests to new immutability/inventory-driven behavior.
tests/test_sql_sales_report_repo.py Adapts repo tests to SalesReport(id=...) shape.
tests/test_sql_delivery_request_repo.py Updates DR persistence tests to use save(dr) instead of save_status.
tests/test_sales_report.py Updates SalesReport construction + removes reports_by_partner test.
tests/test_report_required_policy.py Updates policy tests to use use-cases + helper fixtures.
tests/test_policies.py Adjusts stock projection tests and adds a voided-sales case.
tests/test_partner_inventory.py Adds new tests covering persisted inventory behavior.
tests/test_delivery_request.py Updates tests for immutable transitions (return new DR).
tests/helpers.py Refactors test helpers to use use-cases for DR/SR setup.
tests/conftest.py Extends Context fixture with pi_repo and adjusts repo helpers.
runner/dispatch.py Switches stock command to query persisted partner inventory.
policies/validations.py Removes stock-validation logic (now handled via inventory).
policies/stock_projection.py Typing cleanup (returns dict[str, int]).
policies/report_required.py Minor typing cleanup.
infra/sql/sql_sales_report_repo.py Adds optional autocommit and returns SalesReport(id=...) from get.
infra/sql/sql_partner_inventory_repo.py New SQL repo implementing inventory upsert/get.
infra/sql/sql_delivery_request_repo.py Refactors save_status into save(dr) and ensures DR id is populated on load.
infra/sql/sql_audit_repo.py Adds optional autocommit for transactional audits.
infra/sql/schema.sql Adds partner_inventories table.
domain/sales_report.py Makes SalesReport immutable, adds id, and makes void() return a new instance.
domain/partner_inventory.py New immutable domain entity for inventory changes.
domain/errors.py Introduces DomainError base + adds InsufficientStock.
domain/delivery_request.py Makes DeliveryRequest immutable, adds id, and returns new instances on transitions.
app/use_cases.py Integrates inventory updates into delivered/report/void workflows with manual transactions.
app/queries.py Adds get_partner_inventory() query.
app/context.py Adds pi_repo to Context.
app/bootstrap.py Wires SqlPartnerInventoryRepo into app context creation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@fleuronvilik fleuronvilik marked this pull request as draft March 31, 2026 07:56
- rename partner inventory sale methods to snake_case and update call sites

- fix the inventory upsert conflict key, move InsufficientStock to the proper import, and add the missing SQL semicolon
- skip inventory restoration when no partner inventory row exists

- clean up sales report test helpers and use the created report id in policy tests
@fleuronvilik fleuronvilik marked this pull request as ready for review March 31, 2026 08:17
@fleuronvilik fleuronvilik requested a review from Copilot March 31, 2026 08:18
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 27 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…d_sales_report

Agent-Logs-Url: https://github.com/fleuronvilik/bookflow-core/sessions/80754b8e-0943-4df9-97e7-c077437229e1

Co-authored-by: fleuronvilik <34385716+fleuronvilik@users.noreply.github.com>
- remove unused imports and dead commented test blocks

- simplify delivery request and partner inventory tests for the current helpers
- replace the unused DataIntegrityError path with an inline note in void_sales_report

- document why missing inventory lines are ignored for this project scope
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

app/use_cases.py Outdated
Comment on lines +196 to +200
for it in sr.items:
pi = ctx.pi_repo.get(sr.partner_id, it.book_id)
pi = pi.restore_sales(it.quantity)
ctx.pi_repo.save(pi, autocommit=False)

Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

void_sales_report assumes a partner inventory row always exists (pi = ctx.pi_repo.get(...); pi = pi.restore_sales(...)). If pi is None, this will raise an AttributeError and the caller won’t get the intended domain/infrastructure error explaining the missing inventory lines. Add an explicit None check and raise DataIntegrityError (including partner_id + missing book_ids) before performing any DB writes, as described in the PR metadata/prior thread, so the failure mode is deterministic and actionable.

Copilot uses AI. Check for mistakes.
from .context import Context
from .helpers import get_dr_or_raise, get_sr_or_raise
from .errors import ValidationError
# from infra.errors import DataIntegrityError # leaving it there for now
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a commented-out DataIntegrityError import left in the module. Given infra/errors.py is now part of the PR, either wire it into void_sales_report (and any other relevant integrity checks) or remove this comment to avoid dead/unclear code paths.

Suggested change
# from infra.errors import DataIntegrityError # leaving it there for now

Copilot uses AI. Check for mistakes.
- move sales report inventory restoration into restore_quantities_or_raise

- raise a data integrity error when a report item has no matching inventory line
- cover voiding a sales report after deleting one inventory line

- assert DataIntegrityError is raised with the expected missing book id
- fix grammar in delivery request and sales report error messages

- rename restore_sales to restore_sale for consistency
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 54 to 60
return Context(
catalog=catalog,
dr_repo=SqlDeliveryRequestRepo(conn),
sr_repo=SqlSalesReportRepo(conn),
pi_repo=SqlPartnerInventoryRepo(conn),
audit=SqlAuditRepo(conn),
)
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make_ctx only initializes schema.sql when delivery_requests doesn't exist. Since Context now always includes pi_repo, running against an existing bookflow.db (already has delivery_requests) will not create the new partner_inventories table, leading to runtime failures when stock/inventory code runs. Consider checking for (and creating/migrating) partner_inventories as well, or introducing a simple schema migration step.

Copilot uses AI. Check for mistakes.
missing_items_ids.append(it.book_id)
continue
pi = pi.restore_sale(it.quantity)
ctx.pi_repo.save(pi, autocommit=False)
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restore_quantities_or_raise accepts an autocommit parameter but ignores it and always calls ctx.pi_repo.save(..., autocommit=False). This is misleading for callers and could cause subtle transaction bugs if the function is reused elsewhere. Either remove the parameter or thread it through consistently (and document the expected transaction boundary).

Suggested change
ctx.pi_repo.save(pi, autocommit=False)
ctx.pi_repo.save(pi, autocommit=autocommit)

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +35
if missing_items_ids:
raise DataIntegrityError(
f"Sales report with id {sr.id} contains following items ({', '.join(missing_items_ids)}), for which no inventory line exists"
)
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The raised DataIntegrityError message for missing inventory lines does not include partner_id, even though this error is partner-scoped and the PR description mentions including it. Including sr.partner_id in the message would make debugging much easier (especially if the same sr.id could exist across environments/log streams).

Copilot uses AI. Check for mistakes.
dr.approve()
dr.mark_delivered()
ctx.dr_repo.create(dr)
# Stock entrant: 2 examplaires de b1 et 3 de b2 livrés au partenaire
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: "examplaires" should be "exemplaires".

Suggested change
# Stock entrant: 2 examplaires de b1 et 3 de b2 livrés au partenaire
# Stock entrant: 2 exemplaires de b1 et 3 de b2 livrés au partenaire

Copilot uses AI. Check for mistakes.
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.

Refactoriser la persistance côté repository pour persister l'agrégat complet

3 participants