Record manual stock changes in InventoryJournal
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
Research direction
Start with InventoryManageService and IInventoryManageService, then trace the listed stock-update paths in ProductViewModelService, BaseProductController, and the API command handlers. Review InventoryJournal and InventoryManageServiceTests before estimating how the shared adjustment flow fits. Done means all listed adjustment paths, notification behavior, no-op handling, and the specified tests meet the acceptance criteria.
Written by the indexing model from the issue text.
Description
Problem
Manual stock changes leave no trace. InventoryJournal (src/Core/Grand.Domain/Catalog/InventoryJournal.cs) is written only for shipments: InventoryManageService.InsertInventoryJournal (src/Business/Grand.Business.Catalog/Services/Products/InventoryManageService.cs) is called with ObjectType = nameof(Shipment). When an administrator or an API client edits a quantity, the old value is simply overwritten, so there is no answer to "who changed this, when, from what to what, and why".
Code paths that change StockQuantity / ProductWarehouseInventory / combination stock without a journal entry:
- Product edit -
ProductViewModelService(src/Web/Grand.Web.AdminShared/Services/ProductViewModelService.cs):StockQuantityis mapped from the model on update, andSaveProductWarehouseInventory(~L439) writes per-warehouseStockQuantityand then sums toproduct.StockQuantity. Same flow for create (~L771) and update (~L830). - Bulk edit (grid) - same file, ~L1362-1367:
product.StockQuantity = pModel.StockQuantity. - Attribute combinations - same file, ~L2200-2260:
combination.StockQuantity,PrepareCombinationWarehouseInventory(per-warehouseProductCombinationWarehouseInventory), and the recalculatedproduct.StockQuantity. Also the "generate all combinations" path (BaseProductController, ~L2949 resetsStockQuantity = 0). - Out-of-stock subscription notifications live in this view-model layer (
OutOfStockNotifications, ~L189 and ~L223), so they only fire for panel edits. - Store owner and Vendor panels reuse the same AdminShared services/controllers, so they have the same gap.
- Backend API (
src/Modules/Grand.Module.Api):PUT /ProductandPATCH /Product/{key}->UpdateProductCommandHandlermapsStockQuantityfrom the DTO and callsUpdateProduct; it has its own, partial copy of the back-in-stock logic.POST /Product/{key}/UpdateStock->UpdateProductStockCommandHandlersetsproduct.StockQuantityor theProductWarehouseInventoryrow; it duplicatesOutOfStockNotificationsa third time.ProductAttributeCombinationDto.StockQuantityis part of the DTO, so combination stock can change through product update as well.
Product import (ProductImportDataObject) also sets StockQuantity; it is a bulk path and is probably better handled as a follow-up.
The panels do not display InventoryJournal anywhere (no reference in Grand.Web.*); the collection is currently write-only from shipments and read nowhere.
Proposal
Add a core method to IInventoryManageService, for example:
Task AdjustStock(Product product, ProductAttributeCombination combination, string warehouseId,
int newQuantity, string reason, string reference);
(or a delta-based overload). It should:
- apply the new quantity to the product / warehouse row / combination (and its warehouse row), recompute the aggregates, and call
UpdateStockProductso LowStock / auto-unpublish keep working as today; - insert an
InventoryJournalentry with a newObjectTypefor manual adjustments (e.g."StockAdjustment"),InQty/OutQtyderived from the delta,ProductId,Attributes(combination),WarehouseId,Comments = reason,Reference = who/what(user e-mail,"api"); - do nothing (no journal entry) when the quantity did not change;
- own the out-of-stock subscription notifications: move
OutOfStockNotificationsfromProductViewModelServiceinto core so the panel and every API path share one implementation, and delete the copies in the API handlers.
Route every path listed above through it: product edit and warehouse inventory, bulk edit, combination create/edit, API PUT/PATCH/UpdateStock.
Out of scope / follow-ups
- A journal view per product in the admin panel (read side of
InventoryJournal). - An optional "reason" field in the panel UI (until then
Commentscan be a default text such as "Manual adjustment"). - Journal entries for product import.
Acceptance criteria
-
IInventoryManageServiceexposes a stock adjustment method covering product, warehouse and combination (+ combination warehouse) quantities. - Each adjustment writes one
InventoryJournalentry with a dedicatedObjectType, correctInQty/OutQty,CommentsandReference. - Unchanged quantities produce no entry.
- Admin, Store and Vendor product edit, bulk edit and combination edit go through the new method.
- API
PUT,PATCHandUpdateStockgo through the new method;Referenceidentifies the API caller. - LowStock flag, auto-unpublish / out-of-stock behaviour and back-in-stock notifications behave as before, from a single implementation in core.
- Unit tests in
InventoryManageServiceTestscover product, warehouse and combination adjustments, no-op, and notification triggering.
Notes
- Add an index on
InventoryJournal(ProductId,CreateDateUtc) - needed as soon as a per-product view reads it. - No migration needed for existing data; history starts from the release that ships this.
- Dominant language
- C#
- Stars
- 1.3k
- Forks
- 549
- Avg merge
- 7h 58m
- Merged PRs (30d)
- 52
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from grandnode/grandnode2
-
HTML sitemap lists unpublished pagesPossibly taken @KrzysztofPajak claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
grandnode/grandnode2#883 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 65/100
grandnode/grandnode2#881 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 25/100
grandnode/grandnode2#849 ·
Maintainers usually reply within 1 day
-
grandnode/grandnode2#499 ·
Maintainers usually reply within 1 day
-
grandnode/grandnode2#410 ·
Maintainers usually reply within 1 day
All issues in grandnode/grandnode2
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
stryker-mutator/stryker-net#3892 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
MobiFlight/MobiFlight-Connector#3419 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Kryptos-FR/MarkView.Avalonia#105 ·
Maintainers usually reply within 1 day
-
[辞書]Open提案 辞書
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
microsoft/fluentui-blazor#5410 ·
Maintainers usually reply within 1 day