Compare commits

..

13 Commits

Author SHA1 Message Date
Frappe PR Bot
22247ab7c5 chore(release): Bumped to Version 16.31.1
## [16.31.1](https://github.com/frappe/erpnext/compare/v16.31.0...v16.31.1) (2026-08-06)

### Bug Fixes

* **stock:** drop call to confirm_if_drafts_exist missing on v16 ([#57833](https://github.com/frappe/erpnext/issues/57833)) ([e834910](e834910bfd))
2026-08-06 08:12:42 +00:00
Mihir Kandoi
f8b19f7376 Merge pull request #57834 from frappe/mergify/bp/version-16/pr-57833
fix(stock): drop call to confirm_if_drafts_exist missing on v16 (backport #57833)
2026-08-06 13:40:55 +05:30
Mihir Kandoi
e834910bfd fix(stock): drop call to confirm_if_drafts_exist missing on v16 (#57833)
(cherry picked from commit 626e35135f)
2026-08-06 08:09:41 +00:00
Frappe PR Bot
68ea583a1f chore(release): Bumped to Version 16.31.0
# [16.31.0](https://github.com/frappe/erpnext/compare/v16.30.0...v16.31.0) (2026-08-04)

### Bug Fixes

* **accounts receivable:** made territory field multi select (backport [#57322](https://github.com/frappe/erpnext/issues/57322)) ([#57346](https://github.com/frappe/erpnext/issues/57346)) ([9b452f1](9b452f12b7))
* **accounts:** fetch deferred invoice docs on non-empty `sales_docs` or `purchase_docs` in repost accounting ledger ([#57753](https://github.com/frappe/erpnext/issues/57753)) ([6153202](6153202231))
* **accounts:** skip party dashboard without invoice permission ([ee6955d](ee6955d56c))
* **accounts:** update AU standard chart of accounts (backport [#57145](https://github.com/frappe/erpnext/issues/57145)) ([#57608](https://github.com/frappe/erpnext/issues/57608)) ([1ea1ce1](1ea1ce15d0))
* allow custom remark on reversal journal entry ([b4dfca9](b4dfca9ef1))
* apply the supplier selection to this branch's own mapper module ([e5f8d0c](e5f8d0c84b))
* **assets:** add permission checks on whitelisted methods on `asset_capitalization` ([99f249b](99f249b1a0))
* **banking:** fetch company list from DB instead of boot (backport [#57731](https://github.com/frappe/erpnext/issues/57731)) ([#57739](https://github.com/frappe/erpnext/issues/57739)) ([ca60653](ca6065398c))
* do not fetch a random inventory account when multiple inventory accounts exist (backport [#57626](https://github.com/frappe/erpnext/issues/57626)) ([#57632](https://github.com/frappe/erpnext/issues/57632)) ([34cbd3c](34cbd3c8d5))
* drop row prefix in reserve stock message when row is unknown ([684ae4d](684ae4d762))
* escape data in multiple templates (backport [#57742](https://github.com/frappe/erpnext/issues/57742)) ([#57770](https://github.com/frappe/erpnext/issues/57770)) ([824ae57](824ae57e44))
* escape item code and UOM in the supplier dialog errors ([06a753f](06a753faf3))
* exclude transferred and consumed qty from dashboard reserved stock ([46317b0](46317b063a))
* filter Accounts Receivable by invoice sales partner (backport [#57628](https://github.com/frappe/erpnext/issues/57628)) ([#57648](https://github.com/frappe/erpnext/issues/57648)) ([0e46937](0e46937f60))
* guard against None row in get_stock_balance_for (backport [#57567](https://github.com/frappe/erpnext/issues/57567)) ([283ee6e](283ee6e07b))
* guard scio row lookup in stock entry items_add ([fb6c87d](fb6c87dd0c))
* **italy:** skip e-invoicing for opening invoices ([5e584d1](5e584d1cfb))
* **item_variant:** added permission checks on `enqueue_multiple_variant_creation` ([c7cf9d8](c7cf9d868b))
* keep the bulk supplier field to half the supplier selection dialog ([2c9db13](2c9db13041))
* label the items table in the supplier selection dialog ([187840b](187840b559))
* let Purchase Receipt cancel defer to Frappe's linked-document check (backport [#57592](https://github.com/frappe/erpnext/issues/57592)) ([#57597](https://github.com/frappe/erpnext/issues/57597)) ([16be0f0](16be0f0944))
* link the item and spell out the unit in the supplier dialog errors ([810b9ae](810b9ae28f))
* list the Purchase Orders created per supplier instead of opening one ([d2fe4b6](d2fe4b623c))
* **manufacturing:** reach the whole configurator from tree toolbar actions ([281e92f](281e92fb6e)), closes [#57773](https://github.com/frappe/erpnext/issues/57773)
* open the Purchase Order when the supplier selection creates only one ([e71cef0](e71cef02b3))
* **opportunity:** add validation for positive item quantities ([a9f969e](a9f969e942))
* **payment reconciliation:** correct supplier gain/loss posting ([dc907ad](dc907add40))
* **payment_request:** added permission checks on `resend_payment_email` ([fcbbb25](fcbbb251cf))
* **plant_floor:** add missing perm check on `get_stock_summary` (backport [#57667](https://github.com/frappe/erpnext/issues/57667)) ([#57670](https://github.com/frappe/erpnext/issues/57670)) ([7bc0475](7bc04752fc))
* **pos:** don't double-escape Item Group names in get_item_groups ([#57673](https://github.com/frappe/erpnext/issues/57673)) ([a5544d0](a5544d0bfb))
* preserve UOM conversion factor precision in transactions ([e5999b2](e5999b22c7))
* prevent duplicate shipping charges without cost center ([666b616](666b6167a1))
* **projects:** add project filter ([3ba8313](3ba83134af))
* **projects:** include on hold status in project filters and reports ([d104d8e](d104d8e723))
* **purchase:** reject purchase returns where every item has zero quantity ([032b922](032b922f0c))
* **quotation:** carry forward communications from opportunity at after_insert (backport [#57639](https://github.com/frappe/erpnext/issues/57639)) ([#57643](https://github.com/frappe/erpnext/issues/57643)) ([624a236](624a236f88))
* reject the same Material Request item twice in one supplier selection ([ea770f6](ea770f6a8e))
* resolve version 16 backport conflicts ([e98471d](e98471d9c9))
* resolve version-16 backport conflicts ([697f68d](697f68d1d2))
* respect child warehouse account override in Stock and Account Value Comparison ([#57552](https://github.com/frappe/erpnext/issues/57552)) ([ca657d2](ca657d2629))
* respect quantity precision in material transfer validation ([eb969a5](eb969a5866))
* **sales:** reject sales returns where every item has zero quantity ([aa71cd6](aa71cd695b))
* seed standard Item Groups under the existing tree root ([4ffa950](4ffa950aa3)), closes [#57581](https://github.com/frappe/erpnext/issues/57581)
* **selling:** don't require cancel and delete perms to remove items via Update Items (backport [#57419](https://github.com/frappe/erpnext/issues/57419)) ([#57601](https://github.com/frappe/erpnext/issues/57601)) ([04e1ca8](04e1ca8226))
* set Required By on Purchase Orders created per supplier ([f99d66d](f99d66d578))
* set reservation voucher_qty to voucher demand not reserved qty ([ecccedf](ecccedf0ed))
* source manually created asset value from valuation rate ([b556b01](b556b012f0))
* **stock:** accept every number a reading can be written as ([fccf122](fccf1220f6))
* **stock:** correct secondary item valuation across stock entry purposes ([4ed0374](4ed03748fe)), closes [#57732](https://github.com/frappe/erpnext/issues/57732) [#57735](https://github.com/frappe/erpnext/issues/57735) [#57736](https://github.com/frappe/erpnext/issues/57736) [#57737](https://github.com/frappe/erpnext/issues/57737) [#57738](https://github.com/frappe/erpnext/issues/57738)
* **stock:** keep manufactured item rate at zero when inputs are free  (backport [#57334](https://github.com/frappe/erpnext/issues/57334)) ([#57513](https://github.com/frappe/erpnext/issues/57513)) ([49924dd](49924ddd1d))
* **stock:** read quality inspection readings in the user's number format ([3b7fb68](3b7fb6851a))
* **stock:** resolve backport conflict in quality inspection imports ([656db1c](656db1c2fe))
* **stock:** scope over deliver/receive role check to delivery and receipt overflow ([4713ddd](4713ddd55b))
* **stock:** validate only the variant attributes that changed ([0013908](00139081f6))
* **stock:** validate over delivery/receipt allowance in stock settings ([3f3292c](3f3292ca4a))
* **stock:** value batched packed-item returns from the original bundle  (backport [#57327](https://github.com/frappe/erpnext/issues/57327)) ([#57511](https://github.com/frappe/erpnext/issues/57511)) ([c0ac8aa](c0ac8aaf86))
* sync translations from crowdin ([#57741](https://github.com/frappe/erpnext/issues/57741)) ([285aec3](285aec3164))
* update stock variance account logic which defaults to default expense ([#57656](https://github.com/frappe/erpnext/issues/57656)) ([d09c04a](d09c04a623))
* use payment entry posting date for received amount exchange rate (backport [#57660](https://github.com/frappe/erpnext/issues/57660)) ([#57663](https://github.com/frappe/erpnext/issues/57663)) ([5595d1e](5595d1ed2f))
* validate account frozen date ([17aeb0b](17aeb0b55b))
* warn about existing draft orders before the supplier selection creates more ([93331a1](93331a1cf0))

### Features

* adjust the ordered quantity in the supplier selection dialog ([2c8c375](2c8c375ca6))
* alert when Required By falls back to today ([1adeb66](1adeb66bdc))
* auto-fill subscription accounting dimensions from plan with item fallback (backport [#57615](https://github.com/frappe/erpnext/issues/57615)) ([#57622](https://github.com/frappe/erpnext/issues/57622)) ([ebba4e9](ebba4e9958))
* order only the items ticked in the supplier selection dialog ([d6ee543](d6ee5436b8))
* select a supplier per item when creating Purchase Orders from Material Request ([95f7810](95f7810948))
* set one supplier across every item in the supplier selection dialog ([9b647be](9b647bed5c))
* show the UOM alongside the quantity in the supplier selection dialog ([5aaefec](5aaefec747))
* status based bar colors in Work Order gantt view (backport [#57634](https://github.com/frappe/erpnext/issues/57634)) ([#57636](https://github.com/frappe/erpnext/issues/57636)) ([2d03d80](2d03d80269))
2026-08-04 20:30:34 +00:00
Diptanil Saha
2769a8c69e Merge pull request #57781 from frappe/version-16-hotfix
chore: release v16
2026-08-05 01:58:06 +05:30
Frappe PR Bot
8378b6e203 chore(release): Bumped to Version 16.30.0
# [16.30.0](https://github.com/frappe/erpnext/compare/v16.29.0...v16.30.0) (2026-07-28)

### Bug Fixes

* add permission check for `get_item_details` (backport [#57515](https://github.com/frappe/erpnext/issues/57515)) ([#57551](https://github.com/frappe/erpnext/issues/57551)) ([7a606ab](7a606ab91c))
* bump item doctype modified timestamp so description change syncs on migrate ([2471c5c](2471c5ccbb))
* **crm:** align Opportunity status checks with Quotation statuses (backport [#57489](https://github.com/frappe/erpnext/issues/57489)) ([#57491](https://github.com/frappe/erpnext/issues/57491)) ([ad9870a](ad9870acc1))
* **crm:** clarify the reason why an opportunity cannot be declared as lost (backport [#57495](https://github.com/frappe/erpnext/issues/57495)) ([#57498](https://github.com/frappe/erpnext/issues/57498)) ([3ea19d8](3ea19d8eb1))
* detect the currency column by fieldtype in trends total row ([a30aac8](a30aac87bf))
* enable the 'Include Zero Stock Items' filter by default to show zero-stock items in the Stock Balance report (backport [#57458](https://github.com/frappe/erpnext/issues/57458)) ([#57460](https://github.com/frappe/erpnext/issues/57460)) ([a0af717](a0af717234))
* enhance growth view filtering by validating period keys ([3f87836](3f87836536))
* force-delete repost data file during cleanup (backport [#57245](https://github.com/frappe/erpnext/issues/57245)) ([32baf6a](32baf6a47c))
* get reserved batch qty precision from settings ([95de237](95de2374ed))
* guard against missing is_your_company_address custom field on address ([2e6b4b5](2e6b4b5838))
* Incorrect creation time at the time cancelling an entry causing an issue especially same posting datetime  (backport [#57380](https://github.com/frappe/erpnext/issues/57380)) ([#57397](https://github.com/frappe/erpnext/issues/57397)) ([84813d7](84813d7f46))
* **item:** correct description on deferred revenue/expense ([ee12f8d](ee12f8d2d4))
* **manufacturing:** exclude corrective job cards from semi-FG aggregate ([ebc8482](ebc8482310))
* **manufacturing:** fall back to UOM Conversion Factor in Production Plan (backport [#57553](https://github.com/frappe/erpnext/issues/57553)) ([#57554](https://github.com/frappe/erpnext/issues/57554)) ([8f36753](8f36753705))
* **manufacturing:** scope BOM Creator tree children to the parent row ([339bb0b](339bb0b4ea)), closes [#57311](https://github.com/frappe/erpnext/issues/57311)
* **manufacturing:** sum semi-FG qty across split job cards ([c411b8e](c411b8e471))
* **manufacturing:** update cost of BOMs created via BOM Creator ([f1a0a5e](f1a0a5e1bf))
* map MT940 per-transaction reference from :61: customer_reference ([b9c4f79](b9c4f790bd))
* map pick list customer to delivery note when no sales order ([3f6501b](3f6501b4ff)), closes [#57412](https://github.com/frappe/erpnext/issues/57412)
* migrate stored AR/AP ageing filter to renamed field ([2e5fc38](2e5fc38179))
* pool batch slot values on every run, not only when negative ([5763378](5763378ee1))
* **quotation:** carry forward communications from opportunity (backport [#57507](https://github.com/frappe/erpnext/issues/57507)) ([#57509](https://github.com/frappe/erpnext/issues/57509)) ([f9b3e42](f9b3e42dcd))
* rebalance batch slot values at the pooled rate when driven negative ([8a50572](8a50572786))
* recalculate operating cost on hour rate change in routing ([c182b40](c182b4085b))
* recover failed POS closings (backport [#57203](https://github.com/frappe/erpnext/issues/57203)) ([#57572](https://github.com/frappe/erpnext/issues/57572)) ([9c946eb](9c946eb168))
* rename misleading filter labels in AR/AP reports ([6d856fa](6d856fa632))
* resolve backport conflicts in item doctype ([0ce5040](0ce50407f9))
* respect selected BOM when creating work order for variant item ([#57359](https://github.com/frappe/erpnext/issues/57359)) ([1132eb1](1132eb1a0f))
* respect user permissions in party dashboard company list ([c79f2e4](c79f2e45a8)), closes [frappe/erpnext#57428](https://github.com/frappe/erpnext/issues/57428)
* **setup:** scope manufacturing warehouse filters to company ([f05e8ed](f05e8ed0ce))
* skip stock expense GL entries for non-stock items ([#57518](https://github.com/frappe/erpnext/issues/57518)) ([861c50e](861c50e727))
* **stock:** narrow legacy serial ledger lookup by item (backport [#57499](https://github.com/frappe/erpnext/issues/57499)) ([#57506](https://github.com/frappe/erpnext/issues/57506)) ([59efe72](59efe7299a))
* stop storing raw title template on subcontracting orders ([0863c1e](0863c1e05c))
* **subcontracting:** release raw-material reservation when closing a subcontracting order ([ee75fac](ee75fac9d7))
* **test:** flaky test in exchange rate revaluation ([484ff8e](484ff8e349))
* typeerror in get_batches_by_oldest for mixed batch expiry ([bbe7580](bbe7580c9d))
* update formatting of growth view for FS report ([da3844c](da3844c4df))
* update operating cost when propagating workstation hour rate to routing ([#57504](https://github.com/frappe/erpnext/issues/57504)) ([39d5fd8](39d5fd84db))
* use company currency instead of global default in report (backport [#56561](https://github.com/frappe/erpnext/issues/56561)) ([9e5f77b](9e5f77b57c))

### Features

* block sales invoice submit when customer overdue exceeds threshold (backport [#57230](https://github.com/frappe/erpnext/issues/57230), [#57298](https://github.com/frappe/erpnext/issues/57298)) ([#57438](https://github.com/frappe/erpnext/issues/57438)) ([d3c5e86](d3c5e866f0)), closes [#52960](https://github.com/frappe/erpnext/issues/52960)
* book Expenses Added To Stock GL entries (backport [#57190](https://github.com/frappe/erpnext/issues/57190) + [#57475](https://github.com/frappe/erpnext/issues/57475)) ([#57503](https://github.com/frappe/erpnext/issues/57503)) ([68caa60](68caa60dfa))
* make Shipping Rule Cost Center optional with company default fallback (backport [#57355](https://github.com/frappe/erpnext/issues/57355)) ([#57385](https://github.com/frappe/erpnext/issues/57385)) ([bbd942c](bbd942c600))
2026-07-28 23:31:26 +00:00
Diptanil Saha
eaf95e5c36 Merge pull request #57548 from frappe/version-16-hotfix
chore: release v16
2026-07-29 04:59:39 +05:30
Frappe PR Bot
a5de60c357 chore(release): Bumped to Version 16.29.0
# [16.29.0](https://github.com/frappe/erpnext/compare/v16.28.0...v16.29.0) (2026-07-22)

### Bug Fixes

* add fetch from in production plan material request child table ([2852671](2852671cd5))
* added missing validations for `Dunning Type` (backport [#57224](https://github.com/frappe/erpnext/issues/57224)) ([#57227](https://github.com/frappe/erpnext/issues/57227)) ([3948951](3948951904))
* allow delivery when a batch is reserved across multiple sales orders (backport [#57169](https://github.com/frappe/erpnext/issues/57169)) ([56bbca0](56bbca0203))
* block changing Stock account type when stock ledger entries exist (backport [#57283](https://github.com/frappe/erpnext/issues/57283)) ([#57285](https://github.com/frappe/erpnext/issues/57285)) ([a26296c](a26296ca50))
* consider min order qty in the purchase/transfer flow of production plan (backport [#57204](https://github.com/frappe/erpnext/issues/57204)) ([cbd4f93](cbd4f93f3d))
* correct typo in allow_negative_stock parameter ([e2be05e](e2be05e48b))
* dont overwrite rate with 0 if not found ([aa08f75](aa08f753b4))
* exclude fully-delivered reservations from the conflict message ([555c607](555c607f2f))
* exclude transferred_qty from work order item to pick list item mapping ([c466b49](c466b49d09)), closes [#57253](https://github.com/frappe/erpnext/issues/57253) [#57236](https://github.com/frappe/erpnext/issues/57236) [#56596](https://github.com/frappe/erpnext/issues/56596)
* hide job card field in purchase order item ([20f6ac8](20f6ac81b9))
* keep Standard Cost stock value in step with the standard rate ([fe65882](fe65882e59))
* name every conflicting voucher in the reserved batch error ([839fd5e](839fd5e3b6))
* parallel reposting stalls between scheduler ticks (backport [#57220](https://github.com/frappe/erpnext/issues/57220)) ([#57248](https://github.com/frappe/erpnext/issues/57248)) ([a3bfded](a3bfdede06))
* pass ctx keys get_price_list_rate_for reads, skip rate update on insert ([c3aea9c](c3aea9ca9c))
* **payments:** ensure `payments` app installed on the site in `payment_app_import_guard` (backport [#57342](https://github.com/frappe/erpnext/issues/57342)) ([#57344](https://github.com/frappe/erpnext/issues/57344)) ([2c8c076](2c8c076f6e))
* permission issue (backport [#57112](https://github.com/frappe/erpnext/issues/57112)) (backport [#57142](https://github.com/frappe/erpnext/issues/57142)) ([#57184](https://github.com/frappe/erpnext/issues/57184)) ([0817d10](0817d1064c))
* project % complete field allowing modification when manual method (backport [#57274](https://github.com/frappe/erpnext/issues/57274)) ([#57276](https://github.com/frappe/erpnext/issues/57276)) ([0d53f1a](0d53f1adb7))
* **project:** improved access control for project users (backport [#56675](https://github.com/frappe/erpnext/issues/56675)) ([#57181](https://github.com/frappe/erpnext/issues/57181)) ([b1adec7](b1adec7e9e))
* **report:** handle nonetype error in timesheet billing summary grouping logic ([b7cf3bf](b7cf3bf641))
* rescale batch FIFO slot values on stock reconciliation ([6b3b03f](6b3b03fcd8))
* rescale stock ageing FIFO slot values on stock reconciliation ([c9394c0](c9394c030f))
* resolve float precision before streaming stock ledger entries ([0f25254](0f252542c3))
* restrict jinja globals in process statement of accounts templates (backport [#56458](https://github.com/frappe/erpnext/issues/56458)) ([#57232](https://github.com/frappe/erpnext/issues/57232)) ([5f6952b](5f6952b15c))
* revalue batch reco slots only when the entry covers the full batch ([a4bf506](a4bf50656a))
* round outstanding qty guard consistently with the conflict gate ([f3e1b3f](f3e1b3fca7))
* set correct currency in supplier quotation net rate field ([e24ef84](e24ef847dc))
* show transaction currency symbol in Payment Request schedule dialog and reference table (backport [#57050](https://github.com/frappe/erpnext/issues/57050)) ([#57312](https://github.com/frappe/erpnext/issues/57312)) ([429b58b](429b58b833))
* **stock:** correct stock ageing value for moving average and lifo items ([#56693](https://github.com/frappe/erpnext/issues/56693)) ([02549ef](02549ef8c4))
* strip account number when building account name in COA importer ([e1ebfa7](e1ebfa7163))
* sync process loss percentage when fg qty changes ([beeffee](beeffee8f9))
* sync translations from crowdin ([#57187](https://github.com/frappe/erpnext/issues/57187)) ([827831a](827831a247))
* use system float precision for batch qty comparison ([92217b2](92217b2c45))
* validate buying price list on material request and update item rates on change ([025b2f2](025b2f2922))

### Features

* add on hold status to project ([57a49ce](57a49ce168))
* recalculate valuation rate and stock value from Bin ([a38cbfc](a38cbfc88e))
* **stock:** automatically link portal users to their associated contact profiles for customers and suppliers ([134d63d](134d63de78))
* **stock:** expose all Bin qty fields in Stock Summary and Stock Projected Qty ([0bdf258](0bdf258888))
2026-07-22 00:17:03 +00:00
Diptanil Saha
264bfa188b Merge pull request #57324 from frappe/version-16-hotfix
chore: release v16
2026-07-22 05:45:21 +05:30
Frappe PR Bot
de591661b9 chore(release): Bumped to Version 16.28.0
# [16.28.0](https://github.com/frappe/erpnext/compare/v16.27.0...v16.28.0) (2026-07-15)

### Bug Fixes

* **accounts:** added permission checks on `get_account_balances_coa` (backport [#57107](https://github.com/frappe/erpnext/issues/57107)) ([#57122](https://github.com/frappe/erpnext/issues/57122)) ([e8a5325](e8a532587d))
* allow barcode scan to add and increment items in pick list ([00a646a](00a646ac25))
* apply precision fix inline for v16-hotfix, drop develop-only asset_service refactor ([d3a8e91](d3a8e91cda))
* **asset:** allow asset repair creation for fully depreciated assets ([8658039](8658039e9a))
* **budget-variance:** correct month shift in comparison chart ([703e9a7](703e9a728c))
* guard company logo lookup in default letterheads ([7b54314](7b543142a2))
* **journal-entry:** fetch outstanding on foreign currency ([ec782ee](ec782ee20d))
* **manufacturing:** preserve job card transferred quantity ([d7e9321](d7e9321746))
* match depreciation schedule rows at currency precision to avoid duplicate JEs ([a1fae95](a1fae959ed))
* prevent duplicate material request items in purchase order ([4d951c1](4d951c1cf8))
* set explicit table and logo widths in grey letterhead ([0d80fef](0d80fef3bf))
* **stock:** fix sqlparse token limit in get_bundle_wise_serial_nos ([5a99dd6](5a99dd6016))
* **stock:** set stock_uom on transferred Stock Reservation Entries ([5991ecf](5991ecfa3d))
* **stock:** show qty (company) and qty (warehouse) in sales transactions ([0d9ace7](0d9ace7ab8))
* **tnc:** `get_terms_and_conditions` render_template with `safe_exec` (backport [#56944](https://github.com/frappe/erpnext/issues/56944)) ([#56977](https://github.com/frappe/erpnext/issues/56977)) ([40c85a0](40c85a0087))

### Features

* **balance-sheet:** implement execute_synced_report with full parity to normal report ([636bcbe](636bcbedc0))
* **general-ledger:** implement execute_synced_report with full parity to normal report ([19ec095](19ec095ff8))
* **profit-and-loss:** implement execute_synced_report with full parity to normal report ([21ddb00](21ddb00e20))
* **trial-balance:** implement execute_duckdb with full parity to normal report ([228418b](228418b05f))
* weekly auto-repost of incorrect stock valuation entries (backport [#56637](https://github.com/frappe/erpnext/issues/56637)) ([#56700](https://github.com/frappe/erpnext/issues/56700)) ([bf1b7f2](bf1b7f2bea))
2026-07-15 02:24:51 +00:00
ruthra kumar
9a7e796fd2 Merge pull request #57161 from frappe/version-16-hotfix
chore: release v16
2026-07-15 06:17:14 +05:30
Frappe PR Bot
9d5c7605b8 chore(release): Bumped to Version 16.27.0
# [16.27.0](https://github.com/frappe/erpnext/compare/v16.26.2...v16.27.0) (2026-07-13)

### Bug Fixes

* **`frappe_crm_api`:** handle failure for `after_app_install` and `after_app_uninstall` ([eac3afc](eac3afcd88))
* **accounts:** clear clearance date when amending reconciled voucher ([6f28135](6f281356a6)), closes [#54909](https://github.com/frappe/erpnext/issues/54909)
* **accounts:** retain invoice table on opening invoice creation error (backport [#56353](https://github.com/frappe/erpnext/issues/56353)) ([#57058](https://github.com/frappe/erpnext/issues/57058)) ([4136ca5](4136ca55a7))
* add page numbers to print format footer ([0371e8e](0371e8eaf0))
* added permission checks on various whitelisted functions (backport [#56745](https://github.com/frappe/erpnext/issues/56745)) ([#56946](https://github.com/frappe/erpnext/issues/56946)) ([7ce1289](7ce1289c10))
* address review comments ([056195c](056195ce07))
* auto fetch serial no from previous operation output (backport to v16) ([#56861](https://github.com/frappe/erpnext/issues/56861)) ([17f2de4](17f2de42f3))
* **banking:** allow negative balance in bank statement import (backport [#56959](https://github.com/frappe/erpnext/issues/56959)) ([#57054](https://github.com/frappe/erpnext/issues/57054)) ([835e32c](835e32cec7))
* **banking:** handle blank password protected PDFs and negative amounts in CR/DR columns (backport [#56690](https://github.com/frappe/erpnext/issues/56690)) ([#56694](https://github.com/frappe/erpnext/issues/56694)) ([19c318d](19c318df68))
* **banking:** use custom renderer for translated strings and parser for rules (backport [#56643](https://github.com/frappe/erpnext/issues/56643)) ([#56647](https://github.com/frappe/erpnext/issues/56647)) ([2cb577b](2cb577b912))
* block serialized to non-serialized item change when SABB exists (backport [#56773](https://github.com/frappe/erpnext/issues/56773)) ([#56775](https://github.com/frappe/erpnext/issues/56775)) ([19d03fe](19d03fee46))
* clear stray permission message when item dashboard has no warehouse access ([a39753e](a39753ee08))
* **company:** ignore user permissions for link fields having link to `Account` and `Cost Center` (backport [#56748](https://github.com/frappe/erpnext/issues/56748)) ([#56795](https://github.com/frappe/erpnext/issues/56795)) ([003b655](003b6554c4))
* correct filter handling in Sales Person-wise Transaction Summary + tests (backport [#56783](https://github.com/frappe/erpnext/issues/56783)) ([#56908](https://github.com/frappe/erpnext/issues/56908)) ([24f0989](24f0989ac1))
* **crm_settings:** skip allowed users check when frappe crm is installed locally ([dfe4d5a](dfe4d5ab73))
* display outstanding amount using company default currency (backport [#56785](https://github.com/frappe/erpnext/issues/56785)) ([#57009](https://github.com/frappe/erpnext/issues/57009)) ([bdba4c8](bdba4c8091))
* don't attempt to create SABB for non-serialized / non-batch items (backport [#56627](https://github.com/frappe/erpnext/issues/56627)) ([#56803](https://github.com/frappe/erpnext/issues/56803)) ([8c56a5a](8c56a5ac0c))
* don't treat batch slot at FIFO queue head as qty slot ([20f6dd0](20f6dd0224))
* enforce read permission on ledger preview endpoints ([#57039](https://github.com/frappe/erpnext/issues/57039)) ([5642359](5642359c10))
* fetch payment entry reference amounts from invoice (backport [#56928](https://github.com/frappe/erpnext/issues/56928)) ([#57042](https://github.com/frappe/erpnext/issues/57042)) ([67c85ef](67c85ef0af))
* FIFO queue checks and incorrect entries filter in stock ledger reports ([3571512](3571512101)), closes [#43619](https://github.com/frappe/erpnext/issues/43619)
* **financial_statement:** render columnar financial statements instea… (backport [#56921](https://github.com/frappe/erpnext/issues/56921)) ([#57053](https://github.com/frappe/erpnext/issues/57053)) ([e6a6c13](e6a6c13355))
* for purchases do voucher based reposting (backport [#56601](https://github.com/frappe/erpnext/issues/56601)) ([#56608](https://github.com/frappe/erpnext/issues/56608)) ([e1e6176](e1e6176ddc))
* guard against missing DocType in onboarding steps patch ([#56804](https://github.com/frappe/erpnext/issues/56804)) ([f72289e](f72289e27c))
* **item-attribute:** clear attribute values when marking numeric ([374b340](374b340e73))
* **item:** error on uncommitted input and escape values in variant dialog ([04c834d](04c834d6a9))
* **item:** rework multiple variant dialog for large numeric ranges ([025d0cd](025d0cd7f3))
* make represents company field in purchase invoice ignore user permissions ([4aed77d](4aed77df0c))
* make trend report based-on and group-by column labels translatable ([b8199d8](b8199d88b6))
* map stock_qty in apply_price_list_on_item (backport [#56869](https://github.com/frappe/erpnext/issues/56869)) ([#57052](https://github.com/frappe/erpnext/issues/57052)) ([240fb2c](240fb2c4b8))
* partial delivery note against pick list (backport [#56985](https://github.com/frappe/erpnext/issues/56985)) ([#57006](https://github.com/frappe/erpnext/issues/57006)) ([20255a8](20255a8a7f))
* **patch:** moved create_company_custom_fields from `pre_model_sync` to `post_model_sync` (backport [#56962](https://github.com/frappe/erpnext/issues/56962)) ([#56965](https://github.com/frappe/erpnext/issues/56965)) ([14efd14](14efd14384))
* **payment reconciliation:** honour user permissions on accounting di… ([#56560](https://github.com/frappe/erpnext/issues/56560)) ([8eb92b8](8eb92b8b18))
* precision issue causing reconciliation error ([#54043](https://github.com/frappe/erpnext/issues/54043)) ([20df15b](20df15b3ac))
* prevent repeatable read related concurrency errors ([019b02a](019b02adcd))
* race condition and repeatable read in process pcv ([6d9f5fa](6d9f5fac76))
* recompute transferred qty before deciding work order status ([bc952f4](bc952f40d9))
* remove company default on cost center in stock entry detail ([a1fe6ce](a1fe6cee5d))
* remove incorrect Payable account_type from Customer Deposits in Philippines CoA (backport [#57018](https://github.com/frappe/erpnext/issues/57018)) ([#57061](https://github.com/frappe/erpnext/issues/57061)) ([edfbc71](edfbc71135))
* rename variant item_code/item_name when attribute abbreviation changes ([97cd1e7](97cd1e7144))
* render letter head footer in print formats ([fa4d32d](fa4d32dcdb))
* replay immutable SLE qty for serial/batch bundle valuation (backport [#56814](https://github.com/frappe/erpnext/issues/56814)) ([#56835](https://github.com/frappe/erpnext/issues/56835)) ([2264e25](2264e25cc6))
* resolve conflicts ([3aad7fe](3aad7fee24))
* restore Save button on reverse journal entry (backport [#56770](https://github.com/frappe/erpnext/issues/56770)) ([#56772](https://github.com/frappe/erpnext/issues/56772)) ([4573cd1](4573cd15a9))
* **selling:** update sales order per billed on credit note submission ([224cf19](224cf19f33))
* set conversion_rate on quotation created from customer ([26905bc](26905bc142))
* show only template items in Variant Of filter ([bebe011](bebe011636))
* skip item prices tab render for users without item price read access ([c964811](c964811293))
* skip stock reservation for opted-out production plans ([17598e2](17598e2626)), closes [#56798](https://github.com/frappe/erpnext/issues/56798)
* **stock:** backfill transferred qty for existing pick lists ([903d78c](903d78cc43))
* **stock:** link job card in stock entry created from pick list (backport [#57031](https://github.com/frappe/erpnext/issues/57031)) ([755b9cc](755b9ccbc3))
* **stock:** pick list serial batch posting date ([#57015](https://github.com/frappe/erpnext/issues/57015)) ([7e46be2](7e46be2a33)), closes [#56951](https://github.com/frappe/erpnext/issues/56951)
* **stock:** propagate project from job card to stock entry ([51d2077](51d2077eed))
* **stock:** support quality inspection for stock entry by purpose (backport [#56446](https://github.com/frappe/erpnext/issues/56446)) ([40ca3b5](40ca3b5e5d))
* update BOM operations when routing is changed ([b2e6a39](b2e6a39743))
* update events order by date asc (backport [#56963](https://github.com/frappe/erpnext/issues/56963)) ([#57056](https://github.com/frappe/erpnext/issues/57056)) ([e2fd061](e2fd061b3d))
* update modified timestamp in item.json ([f602ee0](f602ee0e73))
* use live source warehouse valuation for internal transfer purchase receipts (backport [#56431](https://github.com/frappe/erpnext/issues/56431)) ([#56677](https://github.com/frappe/erpnext/issues/56677)) ([1da28f2](1da28f2278))
* validate planned end date is not before planned start date in work order ([745baad](745baad0d1))
* validate reverse GL entries on current date under immutable ledger (backport [#56709](https://github.com/frappe/erpnext/issues/56709)) ([#56752](https://github.com/frappe/erpnext/issues/56752)) ([eea4756](eea4756ce9))
* validate template and its variant in the same Pricing Rule ([159a253](159a2538da))

### Features

* capture user persona during setup (backport [#56705](https://github.com/frappe/erpnext/issues/56705)) ([#56707](https://github.com/frappe/erpnext/issues/56707)) ([d9aa449](d9aa4491f1))
* **crm_settings:** auto-update crm sync settings on frappe crm install and uninstall ([cb2a930](cb2a930a32))
* explain FIFO allocation of fixed Discount Amount on Sales Order ([#56436](https://github.com/frappe/erpnext/issues/56436)) ([62fed1d](62fed1d562))
* **manufacturing:** create material request for raw materials from work order ([#56980](https://github.com/frappe/erpnext/issues/56980)) ([144f92d](144f92d58e))
* **sla:** filter service level agreement link by document type (backport [#56954](https://github.com/frappe/erpnext/issues/56954)) ([#56956](https://github.com/frappe/erpnext/issues/56956)) ([393b5d1](393b5d1f74))
* **stock:** support partial transfer from pick list ([af495ed](af495ed253))

### Performance Improvements

* avoid per-row Warehouse doc fetches in auto reorder job ([39b5e12](39b5e12305))
* batch bin lookups in delivery note stock update ([697e2c0](697e2c0b66))
2026-07-13 07:41:31 +00:00
Diptanil Saha
f94eee3197 Merge pull request #56931 from frappe/version-16-hotfix
chore: release v16
2026-07-13 12:09:52 +05:30
20 changed files with 420 additions and 1700 deletions

View File

@@ -6,7 +6,7 @@ import frappe
from frappe.model.document import Document
from frappe.utils.user import is_website_user
__version__ = "16.26.2"
__version__ = "16.31.1"
def get_default_company(user=None):

View File

@@ -6,10 +6,9 @@ import copy
import frappe
from frappe import _
from frappe.query_builder.functions import Max, Sum
from frappe.utils import add_days, flt, fmt_money, formatdate, get_link_to_form, getdate
from frappe.query_builder.functions import Sum
from frappe.utils import add_days, flt, formatdate, getdate
from erpnext import is_perpetual_inventory_enabled
from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
make_closing_entries,
)
@@ -19,8 +18,6 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
from erpnext.accounts.general_ledger import check_freezing_date, is_immutable_ledger_enabled
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock.doctype.stock_closing_entry.stock_closing_entry import apply_unscoped_filters
from erpnext.stock.utils import get_stock_value_on
class PeriodClosingVoucher(AccountsController):
@@ -142,121 +139,6 @@ class PeriodClosingVoucher(AccountsController):
if account_currency != company_currency:
frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency))
def before_submit(self):
if not self.has_stock_transactions():
return
self.validate_stock_accounts_balance()
self.validate_stock_closing_entry()
def has_stock_transactions(self):
if not is_perpetual_inventory_enabled(self.company):
return False
return bool(
frappe.db.exists(
"Stock Ledger Entry",
{
"company": self.company,
"is_cancelled": 0,
"posting_date": ("<=", self.period_end_date),
},
)
)
def validate_stock_accounts_balance(self):
precision = frappe.get_precision("GL Entry", "debit")
account_balance = flt(self.get_stock_accounts_balance(), precision)
stock_value = flt(
get_stock_value_on(posting_date=self.period_end_date, company=self.company), precision
)
if account_balance == stock_value:
return
currency = frappe.get_cached_value("Company", self.company, "default_currency")
frappe.throw(
_(
"The closing balance {0} of the Stock Asset accounts does not match the closing value {1} of the Stock Balance report as on {2}. Resolve the difference using the Stock Ledger Variance report before closing the period."
).format(
frappe.bold(fmt_money(account_balance, currency=currency)),
frappe.bold(fmt_money(stock_value, currency=currency)),
frappe.bold(formatdate(self.period_end_date)),
),
title=_("Stock Value Mismatch"),
)
def get_stock_accounts_balance(self):
gle = frappe.qb.DocType("GL Entry")
account = frappe.qb.DocType("Account")
stock_accounts = (
frappe.qb.from_(account)
.select(account.name)
.where(
(account.account_type == "Stock")
& (account.company == self.company)
& (account.is_group == 0)
)
)
balance = (
frappe.qb.from_(gle)
.select(Sum(gle.debit - gle.credit))
.where(
(gle.company == self.company)
& (gle.is_cancelled == 0)
& (gle.posting_date <= self.period_end_date)
& gle.account.isin(stock_accounts)
)
).run()
return flt(balance[0][0]) if balance else 0.0
def validate_stock_closing_entry(self):
closing_entry = frappe.db.get_value(
"Stock Closing Entry",
apply_unscoped_filters(
{"company": self.company, "to_date": self.period_end_date, "docstatus": 1}
),
["name", "status", "modified"],
as_dict=True,
)
if not closing_entry:
frappe.throw(
_(
"Create a Stock Closing Entry for the entire company with To Date as {0} before submitting the Period Closing Voucher."
).format(frappe.bold(formatdate(self.period_end_date))),
title=_("Stock Closing Entry Required"),
)
if closing_entry.status != "Completed":
frappe.throw(
_(
"The Stock Closing Entry for {0} is not completed yet. Wait for it to complete before submitting the Period Closing Voucher."
).format(frappe.bold(formatdate(self.period_end_date))),
title=_("Stock Closing Entry In Progress"),
)
self.validate_stock_closing_entry_is_fresh(closing_entry)
def validate_stock_closing_entry_is_fresh(self, closing_entry):
sle = frappe.qb.DocType("Stock Ledger Entry")
last_change = (
frappe.qb.from_(sle)
.select(Max(sle.modified))
.where((sle.company == self.company) & (sle.posting_date <= self.period_end_date))
).run()
if last_change and last_change[0][0] and last_change[0][0] > closing_entry.modified:
frappe.throw(
_(
"Stock transactions were created or modified after the Stock Closing Entry {0} was generated. Regenerate it before submitting the Period Closing Voucher."
).format(get_link_to_form("Stock Closing Entry", closing_entry.name)),
title=_("Stock Closing Entry Outdated"),
)
def on_submit(self):
self.db_set("gle_processing_status", "In Progress")
if frappe.get_single_value("Accounts Settings", "use_legacy_controller_for_pcv"):

View File

@@ -3,7 +3,7 @@
import unittest
import frappe
from frappe.utils import flt, today
from frappe.utils import today
from erpnext.accounts.doctype.finance_book.test_finance_book import create_finance_book
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
@@ -307,218 +307,6 @@ class TestPeriodClosingVoucher(ERPNextTestSuite):
repost_doc.posting_date = today()
repost_doc.save()
def test_stock_validations_before_period_closing(self):
from unittest.mock import patch
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
create_custom_fields(
{
"Stock Closing Entry": [
{
"fieldname": "warehouse",
"label": "Warehouse",
"fieldtype": "Link",
"options": "Warehouse",
}
]
}
)
item = make_item("Test PCV Stock Item", {"is_stock_item": 1})
se = make_stock_entry(
item_code=item.name,
qty=10,
rate=100,
to_warehouse="Stores - TPC",
company="Test PCV Company",
posting_date="2021-03-15",
)
pcv = self.make_period_closing_voucher(posting_date="2021-03-31", submit=False)
self.assertRaisesRegex(frappe.ValidationError, "Create a Stock Closing Entry", pcv.submit)
sce = frappe.get_doc(
{
"doctype": "Stock Closing Entry",
"company": "Test PCV Company",
"from_date": pcv.period_start_date,
"to_date": pcv.period_end_date,
"warehouse": "Stores - TPC",
}
).insert()
with patch("erpnext.stock.doctype.stock_closing_entry.stock_closing_entry.enqueue"):
sce.submit()
sce.db_set("status", "Completed")
pcv.reload()
self.assertRaisesRegex(frappe.ValidationError, "Create a Stock Closing Entry", pcv.submit)
frappe.db.set_value("Stock Closing Entry", sce.name, {"warehouse": None, "status": "In Progress"})
pcv.reload()
self.assertRaisesRegex(frappe.ValidationError, "is not completed yet", pcv.submit)
sce.create_stock_closing_balance_entries()
sce.db_set("status", "Completed")
sle = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_no": se.name},
["name", "stock_value_difference"],
as_dict=1,
)
frappe.db.set_value(
"Stock Ledger Entry", sle.name, "stock_value_difference", sle.stock_value_difference + 100
)
pcv.reload()
self.assertRaisesRegex(frappe.ValidationError, "does not match", pcv.submit)
frappe.db.set_value(
"Stock Ledger Entry", sle.name, "stock_value_difference", sle.stock_value_difference
)
pcv.reload()
self.assertRaisesRegex(frappe.ValidationError, "Regenerate", pcv.submit)
self.rebuild_stock_closing_balance(sce)
pcv.reload()
pcv.submit()
self.assertEqual(pcv.docstatus, 1)
def test_batch_valuation_seeded_from_stock_closing_after_period_closing(self):
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import (
get_batch_from_bundle,
)
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
item = make_item(
"Test PCV Batch Item",
{
"is_stock_item": 1,
"has_batch_no": 1,
"create_new_batch": 1,
"batch_number_series": "TPCVB.####",
},
)
se1 = make_stock_entry(
item_code=item.name,
qty=10,
rate=100,
to_warehouse="Stores - TPC",
company="Test PCV Company",
posting_date="2021-03-15",
)
batch_no = get_batch_from_bundle(se1.items[0].serial_and_batch_bundle)
make_stock_entry(
item_code=item.name,
qty=10,
rate=200,
to_warehouse="Stores - TPC",
company="Test PCV Company",
posting_date="2021-06-15",
batch_no=batch_no,
)
pcv = self.make_period_closing_voucher(posting_date="2021-03-31", submit=False)
sce = self.make_completed_stock_closing_entry(pcv.period_start_date, pcv.period_end_date)
pcv.reload()
pcv.submit()
outward = make_stock_entry(
item_code=item.name,
qty=5,
from_warehouse="Stores - TPC",
company="Test PCV Company",
posting_date="2022-04-01",
batch_no=batch_no,
)
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_no": outward.name, "is_cancelled": 0},
"stock_value_difference",
)
self.assertEqual(flt(stock_value_difference, 2), -750.0)
self.assertRaisesRegex(
frappe.ValidationError,
"frozen",
make_stock_entry,
item_code=item.name,
qty=1,
rate=100,
to_warehouse="Stores - TPC",
company="Test PCV Company",
posting_date="2021-05-01",
)
self.assertRaisesRegex(frappe.ValidationError, "frozen", se1.cancel)
self.assertRaisesRegex(frappe.ValidationError, "closed accounting period", sce.cancel)
def test_period_closing_blocks_stale_stock_closing_entry(self):
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
item = make_item("Test PCV Stock Item", {"is_stock_item": 1})
make_stock_entry(
item_code=item.name,
qty=10,
rate=100,
to_warehouse="Stores - TPC",
company="Test PCV Company",
posting_date="2021-03-15",
)
pcv = self.make_period_closing_voucher(posting_date="2021-03-31", submit=False)
sce = self.make_completed_stock_closing_entry(pcv.period_start_date, pcv.period_end_date)
make_stock_entry(
item_code=item.name,
qty=5,
rate=100,
to_warehouse="Stores - TPC",
company="Test PCV Company",
posting_date="2021-05-01",
)
pcv.reload()
self.assertRaisesRegex(frappe.ValidationError, "Regenerate", pcv.submit)
self.rebuild_stock_closing_balance(sce)
pcv.reload()
pcv.submit()
self.assertEqual(pcv.docstatus, 1)
def make_completed_stock_closing_entry(self, from_date, to_date):
from unittest.mock import patch
sce = frappe.get_doc(
{
"doctype": "Stock Closing Entry",
"company": "Test PCV Company",
"from_date": from_date,
"to_date": to_date,
}
).insert()
with patch("erpnext.stock.doctype.stock_closing_entry.stock_closing_entry.enqueue"):
sce.submit()
sce.create_stock_closing_balance_entries()
sce.db_set("status", "Completed")
return sce
def rebuild_stock_closing_balance(self, sce):
sce.remove_stock_closing()
sce.create_stock_closing_balance_entries()
sce.db_set("status", "Completed")
def make_period_closing_voucher(self, posting_date, submit=True):
surplus_account = create_account()
cost_center = create_cost_center("Test Cost Center 1")

View File

@@ -1180,16 +1180,7 @@ frappe.ui.form.on("Sales Invoice", {
}
frm.set_df_property("update_stock", "read_only", frm.doc.has_subcontracted);
// frm.set_df_property mutates a per-document copy, not the doctype's shared field
// metadata, so this always reflects the original (Customize Form) hidden value.
const hidden_by_customization = cint(
frappe.meta.get_docfield("Sales Invoice", "update_stock")?.hidden
);
frm.set_df_property(
"update_stock",
"hidden",
cint(frm.doc.has_subcontracted) || hidden_by_customization
);
frm.toggle_display("update_stock", !frm.doc.has_subcontracted);
},
});

View File

@@ -254,9 +254,6 @@ class Subscription(Document):
"""
Sets the status of the `Subscription`
"""
if self.status == "Cancelled":
return
if self.is_trialling():
self.status = "Trialing"
elif (
@@ -608,11 +605,6 @@ class Subscription(Document):
1. `process_for_active`
2. `process_for_past_due`
"""
# Snapshot before update_subscription_period() below can roll this forward,
# so the cancel_at_period_end check further down still targets the period
# that just ended, not the next one.
current_period_end = self.current_invoice_end
if not self.is_current_invoice_generated(
self.current_invoice_start, self.current_invoice_end
) and self.can_generate_new_invoice(posting_date):
@@ -633,8 +625,8 @@ class Subscription(Document):
self.update_subscription_period()
if self.cancel_at_period_end and (
getdate(posting_date) >= getdate(current_period_end)
or (self.end_date and getdate(posting_date) >= getdate(self.end_date))
getdate(posting_date) >= getdate(self.current_invoice_end)
or getdate(posting_date) >= getdate(self.end_date)
):
self.cancel_subscription()

View File

@@ -614,32 +614,6 @@ class TestSubscription(ERPNextTestSuite):
self.assertRaises(frappe.ValidationError, subscription.process, posting_date=add_days(start_date, 7))
def test_subscription_cancels_at_period_end_without_end_date(self):
# https://github.com/frappe/erpnext/issues/57761 -- generate_invoice() rolls
# current_invoice_end forward to the next period before this check runs, so
# with no end_date to fall back on, cancel_at_period_end must compare
# against the period that just ended, not the (already advanced) next one.
create_plan(
plan_name="_Test plan name 11",
cost=80,
currency="INR",
billing_interval="Day",
billing_interval_count=3,
)
subscription = create_subscription(
start_date=nowdate(),
cancel_at_period_end=1,
generate_invoice_at="End of the current subscription period",
plans=[{"plan": "_Test plan name 11", "qty": 1}],
)
self.assertEqual(len(subscription.invoices), 0)
period_end = subscription.current_invoice_end
subscription.process(posting_date=period_end)
self.assertEqual(subscription.status, "Cancelled")
self.assertEqual(len(subscription.invoices), 1)
def test_invoice_generated_when_scheduler_runs_one_day_late(self):
# The trigger date (period end) is long past, yet catch-up still bills the period
# on creation (Bug 1: the check is `>= trigger`, not `== trigger`).
@@ -800,38 +774,6 @@ class TestSubscription(ERPNextTestSuite):
subscription.reload()
self.assertEqual(subscription.status, "Active")
def test_cancelled_subscription_stays_cancelled_after_payment_and_reprocess(self):
# https://github.com/frappe/erpnext/issues/57761
subscription = create_subscription(
start_date=nowdate(),
generate_invoice_at="Beginning of the current subscription period",
submit_invoice=1,
cancel_at_period_end=1,
)
subscription.process(posting_date=nowdate())
invoice = subscription.get_current_invoice()
self.assertGreater(invoice.outstanding_amount, 0)
subscription.cancel_subscription()
self.assertEqual(subscription.status, "Cancelled")
cancelation_date = getdate(subscription.cancelation_date)
self.assertIsNotNone(cancelation_date)
payment_entry = get_payment_entry(invoice.doctype, invoice.name, bank_account="_Test Bank - _TC")
payment_entry.reference_no = "12345"
payment_entry.reference_date = nowdate()
payment_entry.submit()
subscription.reload()
self.assertEqual(subscription.status, "Cancelled")
self.assertEqual(getdate(subscription.cancelation_date), cancelation_date)
invoice_count = len(subscription.invoices)
subscription.process()
subscription.reload()
self.assertEqual(subscription.status, "Cancelled")
self.assertEqual(len(subscription.invoices), invoice_count)
def test_first_invoice_generated_on_create_for_prepaid(self):
subscription = create_subscription(
start_date=nowdate(),

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2026-08-02 10:09+0000\n"
"PO-Revision-Date: 2026-08-07 10:33\n"
"PO-Revision-Date: 2026-08-03 09:00\n"
"Last-Translator: hello@frappe.io\n"
"Language-Team: Persian\n"
"MIME-Version: 1.0\n"
@@ -6610,7 +6610,7 @@ msgstr ""
#. DocType 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
msgid "Auto match and set the Party in Bank Transactions"
msgstr "مطابقت خودکار و تنظیم طرف در تراکنش‌های بانکی"
msgstr "مطابقت خودکار و تنظیم طرف در معاملات بانکی"
#. Label of the reorder_section (Section Break) field in DocType 'Item'
#: erpnext/stock/doctype/item/item.json
@@ -7781,7 +7781,7 @@ msgstr "تراکنش بانکی"
#: erpnext/accounts/doctype/bank/bank.json
#: erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.json
msgid "Bank Transaction Mapping"
msgstr "نگاشت تراکنش‌های بانکی"
msgstr "نگاشت معاملات بانکی"
#. Name of a DocType
#: erpnext/accounts/doctype/bank_transaction_payments/bank_transaction_payments.json
@@ -13782,7 +13782,7 @@ msgstr ""
#. Label of the created_through_portal (Check) field in DocType 'Appointment'
#: erpnext/crm/doctype/appointment/appointment.json
msgid "Created through Portal"
msgstr "ایجاد شده از طریق پورتال"
msgstr ""
#: erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py:251
msgid "Created {0} scorecards for {1} between:"
@@ -16358,7 +16358,7 @@ msgstr "سرنخ ها و آدرس ها را حذف کنید"
#. in DocType 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Delete Permanently"
msgstr "حذف دائمی"
msgstr ""
#. Label of the delete_transactions_status (Select) field in DocType
#. 'Transaction Deletion Record'
@@ -18340,7 +18340,7 @@ msgstr "تکرار ردیف {0} با همان {1}"
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:110
msgid "Duplicate vouchers found. Remove the duplicate vouchers to continue to repost."
msgstr "سندهای مالی تکراری پیدا شدند. برای ادامه‌ی بازثبت، سندهای مالی تکراری را حذف کنید."
msgstr ""
#: erpnext/accounts/doctype/pricing_rule/pricing_rule.py:157
msgid "Duplicate {0} found in the table"
@@ -18913,7 +18913,7 @@ msgstr "برای رزرو موجودی جزئی، Allow Partial Reservation را
#. Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Enable Appointment Booking Through Portal"
msgstr "فعال‌سازی رزرو وقت ملاقات از طریق پورتال"
msgstr ""
#. Label of the enable_scheduling (Check) field in DocType 'Appointment Booking
#. Settings'
@@ -20481,7 +20481,7 @@ msgstr "نگاشت فیلد"
#. Transaction Mapping'
#: erpnext/accounts/doctype/bank_transaction_mapping/bank_transaction_mapping.json
msgid "Field in Bank Transaction"
msgstr "فیلد در تراکنش‌های بانکی"
msgstr "فیلد در معاملات بانکی"
#: erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py:95
msgid "Fieldname Conflict"
@@ -23296,7 +23296,7 @@ msgstr "لیست تعطیلات"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:89
msgid "Holiday List - {0} is not valid for current date."
msgstr "لیست تعطیلات - {0} برای تاریخ فعلی معتبر نیست."
msgstr ""
#. Label of the holiday_list_name (Data) field in DocType 'Holiday List'
#: erpnext/setup/doctype/holiday_list/holiday_list.json
@@ -23790,7 +23790,7 @@ msgstr "اگر آیتم‌ها موجود هستند، مراحل انتقال
#. (Link) field in DocType 'Stock Settings'
#: erpnext/stock/doctype/stock_settings/stock_settings.json
msgid "If mentioned, the system will allow only the users with this Role to create or modify any stock transaction earlier than the latest stock transaction for a specific item and warehouse. If set as blank, it allows all users to create/edit back-dated transactions."
msgstr "در صورت ذکر، سیستم فقط به کاربرانی که این نقش را دارند اجازه می‌دهد هرگونه تراکنش موجودی را قبل از آخرین تراکنش موجودی برای یک کالا و انبار خاص ایجاد یا اصلاح کنند. اگر خالی تنظیم شود، به همه کاربران اجازه می‌دهد تراکنش‌های تاریخ گذشته را ایجاد/ویرایش کنند."
msgstr "در صورت ذکر شده، این سیستم فقط به کاربران دارای این نقش اجازه می‌دهد تا هر تراکنش موجودی را زودتر از آخرین تراکنش موجودی برای یک کالا و انبار خاص ایجاد یا اصلاح کنند. اگر به صورت خالی تنظیم شود، به همه کاربران اجازه می‌دهد تا تراکنش‌های قدیمی را ایجاد/ویرایش کنند."
#. Description of the 'To Package No.' (Int) field in DocType 'Packing Slip'
#: erpnext/stock/doctype/packing_slip/packing_slip.json
@@ -24268,7 +24268,7 @@ msgstr "به دقیقه"
#. DocType 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "In Minutes (min: 15 mins, max: 60 mins)"
msgstr "به دقیقه (حداقل: ۱۵ دقیقه، حداکثر: ۶۰ دقیقه)"
msgstr ""
#: erpnext/accounts/report/accounts_payable/accounts_payable.js:149
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:181
@@ -24575,7 +24575,7 @@ msgstr "شامل آیتم‌های غیر موجودی"
#: erpnext/accounts/doctype/bank_clearance/bank_clearance.json
#: erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js:45
msgid "Include POS Transactions"
msgstr "شامل تراکنش‌های POS"
msgstr "شامل معاملات POS"
#: erpnext/accounts/doctype/pos_invoice/pos_invoice.py:205
msgid "Include Payment"
@@ -27931,7 +27931,7 @@ msgstr "آیتم {0} را نمی‌توان به عنوان یک زیر مونت
#: erpnext/stock/doctype/material_request/material_request.py:694
msgid "Item {0} cannot be ordered more than once"
msgstr "آیتم {0} را نمی‌توان بیش از یک بار سفارش داد"
msgstr ""
#: erpnext/manufacturing/doctype/blanket_order/blanket_order.py:197
msgid "Item {0} cannot be ordered more than {1} against Blanket Order {2}."
@@ -33211,7 +33211,7 @@ msgstr "از طریق ایمیل اطلاع دهید"
#. Label of the reorder_email_notify (Check) field in DocType 'Stock Settings'
#: erpnext/stock/doctype/stock_settings/stock_settings.json
msgid "Notify by email on creation of automatic Material Request"
msgstr "اطلاع رسانی از طریق ایمیل در مورد ایجاد درخواست خودکار مواد"
msgstr ""
#. Description of the 'Notify Via Email' (Check) field in DocType 'Appointment
#. Booking Settings'
@@ -35187,7 +35187,7 @@ msgstr "تنظیمات POS"
#. Label of the pos_invoices (Table) field in DocType 'POS Closing Entry'
#: erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
msgid "POS Transactions"
msgstr "تراکنش‌های POS"
msgstr "معاملات POS"
#: erpnext/selling/page/point_of_sale/pos_controller.js:178
msgid "POS has been closed at {0}. Please refresh the page."
@@ -38419,7 +38419,7 @@ msgstr "لطفاً یک یادداشت تحویل را انتخاب کنید"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:81
msgid "Please select a Holiday List to enable Appointment Scheduling."
msgstr "لطفا برای فعال کردن زمان‌بندی قرار ملاقات، یک لیست تعطیلات انتخاب کنید."
msgstr ""
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py:153
msgid "Please select a Subcontracting Purchase Order."
@@ -38500,7 +38500,7 @@ msgstr "لطفاً یک سفارش خرید معتبر که برای پیمان
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1355
msgid "Please select a valid {0}"
msgstr "لطفا یک {0} معتبر انتخاب کنید"
msgstr ""
#: erpnext/selling/doctype/quotation/quotation.js:245
msgid "Please select a value for {0} quotation_to {1}"
@@ -42679,7 +42679,7 @@ msgstr "RFQ برای {0} مجاز نیست به دلیل رتبه کارت ام
#. Label of the auto_indent (Check) field in DocType 'Stock Settings'
#: erpnext/stock/doctype/stock_settings/stock_settings.json
msgid "Raise Material Request when stock reaches re-order level"
msgstr "ثبت درخواست مواد زمانی که موجودی به سطح سفارش مجدد رسید"
msgstr ""
#. Label of the complaint_raised_by (Data) field in DocType 'Warranty Claim'
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
@@ -43312,7 +43312,7 @@ msgstr ""
#: erpnext/stock/doctype/bin/bin.js:10
msgid "Recalculate Values"
msgstr "محاسبه مجدد مقادیر"
msgstr ""
#. Option for the 'Status' (Select) field in DocType 'Asset'
#. Option for the 'Purpose' (Select) field in DocType 'Asset Movement'
@@ -44513,7 +44513,7 @@ msgstr "بازنشر در پس‌زمینه شروع شد"
#. Items'
#: erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json
msgid "Reposted"
msgstr "بازثبت شد"
msgstr ""
#. Label of the reposting_data_file (Attach) field in DocType 'Repost Item
#. Valuation'
@@ -44541,7 +44541,7 @@ msgstr ""
#. 'Repost Accounting Ledger Items'
#: erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json
msgid "Reposting Status"
msgstr "وضعیت بازثبت"
msgstr ""
#. Label of the vouchers_based_on_item_and_warehouse_section (Section Break)
#. field in DocType 'Repost Item Valuation'
@@ -45060,7 +45060,7 @@ msgstr "انبار رزرو شده برای آیتم {item_code} در مواد
#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:198
msgid "Reserved for POS Transactions"
msgstr "برای تراکنش‌های POS رزرو شده است"
msgstr "برای معاملات POS رزرو شده است"
#: erpnext/stock/report/stock_projected_qty/stock_projected_qty.py:177
msgid "Reserved for Production"
@@ -48420,7 +48420,7 @@ msgstr "زمانبند غیرفعال است. نمی‌توان حساب‌ها
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:232
msgid "Scheduler is inactive. Reposting will only run once background jobs are processed."
msgstr "زمان‌بند غیرفعال است. ارسال مجدد فقط زمانی اجرا می‌شود که کارهای پس‌زمینه پردازش شوند."
msgstr ""
#. Label of the schedules (Table) field in DocType 'Maintenance Schedule'
#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json
@@ -48851,7 +48851,7 @@ msgstr "انتخاب آدرس تامین کننده"
#: erpnext/stock/doctype/material_request/material_request.js:448
msgid "Select Supplier for Items"
msgstr "انتخاب تامین کننده برای آیتم‌ها"
msgstr ""
#: erpnext/stock/doctype/batch/batch.js:150
msgid "Select Target Warehouse"
@@ -48905,7 +48905,7 @@ msgstr "یک تامین کننده انتخاب کنید"
#: erpnext/stock/doctype/material_request/material_request.js:552
#: erpnext/stock/doctype/material_request/material_request.py:699
msgid "Select a Supplier for Item {0}"
msgstr "انتخاب یک تأمین‌کننده برای آیتم {0}"
msgstr ""
#: banking/src/components/features/BankReconciliation/MatchAndReconcile.tsx:49
msgid "Select a bank account to reconcile"
@@ -48946,7 +48946,7 @@ msgstr "از هر مجموعه یک آیتم را برای استفاده در
#: erpnext/stock/doctype/material_request/material_request.js:539
#: erpnext/stock/doctype/material_request/material_request.py:680
msgid "Select at least one Item"
msgstr "حداقل یک آیتم را انتخاب کنید"
msgstr ""
#: erpnext/stock/doctype/item/item.js:944
msgid "Select at least one attribute value."
@@ -49265,7 +49265,7 @@ msgstr "ارسال با پیوست"
#: erpnext/accounts/doctype/payment_request/payment_request.js:51
#: erpnext/accounts/doctype/payment_request/payment_request.js:55
msgid "Sending Email"
msgstr "ارسال ایمیل"
msgstr ""
#. Option for the 'Detected Amount Format' (Select) field in DocType 'Bank
#. Statement Import Log'
@@ -50094,7 +50094,7 @@ msgstr "تنظیم تامین کننده"
#: erpnext/stock/doctype/material_request/material_request.js:455
msgid "Set Supplier for All Items"
msgstr "تنظیم تأمین‌کننده برای همه آیتم‌ها"
msgstr ""
#. Label of the set_target_warehouse (Link) field in DocType 'Sales Invoice'
#. Label of the set_warehouse (Link) field in DocType 'Purchase Order'
@@ -53005,7 +53005,7 @@ msgstr "پیش‌فاکتور خود را ارسال کنید"
#: erpnext/manufacturing/doctype/job_card/job_card.py:1524
msgid "Submitted Job Card cannot be processed."
msgstr "کارت کار ارسال‌شده قابل پردازش نیست."
msgstr "کارت شغلی ارسال‌شده قابل پردازش نیست."
#. Label of the subscription_section (Section Break) field in DocType 'Payment
#. Request'
@@ -55339,7 +55339,7 @@ msgstr "ردیف‌های زیر تکراری هستند:"
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:130
msgid "The following vouchers are not submitted: {0}"
msgstr "سندهای مالی زیر ارسال نمی‌شوند: {0}"
msgstr ""
#: erpnext/stock/doctype/material_request/material_request.py:1062
msgid "The following {0} were created: {1}"
@@ -55851,7 +55851,7 @@ msgstr "این سند توسط {0} {1} برای مورد {4} بیش از حد م
#: erpnext/templates/emails/appointment_confirmed.html:6
msgid "This email was sent from {0}"
msgstr "این ایمیل از {0} ارسال شده است"
msgstr ""
#: erpnext/stock/doctype/delivery_note/delivery_note.js:496
msgid "This field is used to set the 'Customer'."
@@ -55997,7 +55997,7 @@ msgstr "این فیلتر مورد قبلاً برای {0} اعمال شده ا
#: erpnext/templates/emails/confirm_appointment.html:4
msgid "This link is valid for {0} minutes"
msgstr "این لینک به مدت {0} دقیقه معتبر است"
msgstr ""
#: erpnext/www/banking.py:35
msgid "This method is only meant for developer mode"
@@ -56021,7 +56021,7 @@ msgstr "این گزینه برای ویرایش فیلدهای «تاریخ ار
#. level' (Check) field in DocType 'Stock Settings'
#: erpnext/stock/doctype/stock_settings/stock_settings.json
msgid "This option is useful if you want to ensure a constant supply of raw materials/products and avoid shortage. A Material Request will be raised automatically when stock reached the re-order level defined in the Item form."
msgstr "این گزینه در صورتی مفید است که بخواهید از تأمین مداوم مواد اولیه/محصولات اطمینان حاصل کنید و از کمبود جلوگیری کنید. درخواست مواد به طور خودکار زمانی که موجودی به سطح سفارش مجدد تعریف شده در فرم کالا برسد، ایجاد می‌شود."
msgstr ""
#: banking/src/components/features/BankReconciliation/IncorrectlyClearedEntries.tsx:180
msgid "This report shows all entries in the system where the <strong>clearance date is before the posting date</strong> which is incorrect."
@@ -56118,7 +56118,7 @@ msgstr "این مقدار باید زمانی استفاده شود که هیچ
#: erpnext/www/book_appointment/verify/index.py:18
msgid "This verification link is invalid. Please book the appointment again."
msgstr "این لینک تأیید نامعتبر است. لطفاً دوباره وقت ملاقات رزرو کنید."
msgstr ""
#: banking/src/components/features/Settings/Preferences.tsx:86
msgid "This will automatically run transaction matching rules on unreconciled transactions every hour."
@@ -58546,7 +58546,7 @@ msgstr ""
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:375
msgid "Unable to Repost Accounting Ledger"
msgstr "امکان بازثبت دفتر حسابداری وجود ندارد"
msgstr ""
#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:477
msgid "Unable to fetch DocType details. Please contact system administrator."
@@ -60036,12 +60036,12 @@ msgstr "سرمایه‌گذاری خطرپذیر"
#. 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Verification Link Expiry Duration"
msgstr "مدت زمان انقضای لینک تأیید"
msgstr ""
#. Label of the verification_token (Data) field in DocType 'Appointment'
#: erpnext/crm/doctype/appointment/appointment.json
msgid "Verification Token"
msgstr "توکن تأیید"
msgstr ""
#: erpnext/www/book_appointment/verify/index.html:15
msgid "Verification failed please check the link"
@@ -60049,7 +60049,7 @@ msgstr "تأیید انجام نشد لطفاً پیوند را بررسی کن
#: erpnext/www/book_appointment/verify/index.py:38
msgid "Verification link has expired."
msgstr "لینک تأیید اعتبار منقضی شده است."
msgstr ""
#. Label of the verified_by (Data) field in DocType 'Quality Inspection'
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json
@@ -60907,7 +60907,7 @@ msgstr ""
#: erpnext/templates/emails/appointment_confirmed.html:3
msgid "We look forward to meeting you"
msgstr "مشتاق دیدار شما هستیم"
msgstr ""
#: banking/src/pages/BankStatementImporter.tsx:169
msgid "We support uploading CSV, XLSX, XLS and PDF files. Please make sure the file contains the correct columns."

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2026-08-02 10:09+0000\n"
"PO-Revision-Date: 2026-08-06 10:01\n"
"PO-Revision-Date: 2026-08-03 09:00\n"
"Last-Translator: hello@frappe.io\n"
"Language-Team: Croatian\n"
"MIME-Version: 1.0\n"
@@ -152,7 +152,7 @@ msgstr "% Završeno Metoda"
#: erpnext/projects/doctype/project/project.py:226
msgid "% Complete must be between 0 and 100"
msgstr "% dovršenosti mora biti između 0 i 100"
msgstr ""
#. Label of the percent_complete (Percent) field in DocType 'Project'
#: erpnext/projects/doctype/project/project.json
@@ -349,7 +349,7 @@ msgstr "'Ažuriraj Zalihe' ne može se provjeriti za prodaju osnovne Imovine"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:112
msgid "'Verification Link Expiry Duration' must be between 15 to 60 minutes."
msgstr "'Trajanje Važenja Verifikacijske Poveznice' mora biti između 15 i 60 minuta."
msgstr ""
#: erpnext/accounts/doctype/bank_account/bank_account.py:79
msgid "'{0}' account is already used by {1}. Use another account."
@@ -1118,7 +1118,7 @@ msgstr "Onemogućeni Paket Artikal ne može se odabrati u transakcijama."
#: erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py:636
msgid "A draft reverse journal for {0} has been created: {1}"
msgstr "Nacrt obrnutog naloga knjiženja za {0} je izrađen: {1}"
msgstr ""
#: erpnext/stock/doctype/delivery_trip/delivery_trip.py:59
msgid "A driver must be set to submit."
@@ -1163,7 +1163,7 @@ msgstr "Kontrola Kvaliteta mora biti izvršena prije izdavanja Nabavnog Računa
#: erpnext/stock/doctype/material_request/material_request.js:476
msgid "A separate Purchase Order is created for each Supplier."
msgstr "Za svakog Dobavljača izrađuje se zasebni Nalog Nabave."
msgstr ""
#: erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py:96
msgid "A template with tax category {0} already exists. Only one template is allowed with each tax category"
@@ -1176,7 +1176,7 @@ msgstr "Distributer / trgovac / komisionar / podružnica / preprodavač treće s
#: erpnext/crm/doctype/appointment/appointment.py:70
msgid "A verified appointment cannot be moved back to 'Unverified' status."
msgstr "Potvrđeni termin se ne može vratiti u status 'Neverificirano'."
msgstr ""
#. Option for the 'Blood Group' (Select) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json
@@ -2367,7 +2367,7 @@ msgstr "Radnja je Pokrenuta"
#. DocType 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Action for Expired Unverified Appointments"
msgstr "Radnja za Istekle Nepotvrđene Termine"
msgstr ""
#. Label of the action_if_accumulated_monthly_budget_exceeded (Select) field in
#. DocType 'Budget'
@@ -2913,7 +2913,7 @@ msgstr "Dodaj sve račune na koje želite podijeliti transakciju."
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:92
msgid "Add atleast one voucher to repost."
msgstr "Dodaj barem jedan verifikat za ponovno knjiženje."
msgstr ""
#: erpnext/www/book_appointment/index.html:42
msgid "Add details"
@@ -3430,7 +3430,7 @@ msgstr "Iznos Predujma"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:93
msgid "Advance Booking Days is mandatory for Appointment Scheduling."
msgstr "Prethodna Rezervacija Dana je obavezna za Zakazivanje Termina."
msgstr ""
#. Label of the advance_paid (Currency) field in DocType 'Sales Order'
#: erpnext/selling/doctype/sales_order/sales_order.json
@@ -3753,7 +3753,7 @@ msgstr "Dob ({0})"
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:102
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:28
msgid "Age as on"
msgstr "Dob na"
msgstr ""
#. Label of the ageing_based_on (Select) field in DocType 'Process Statement Of
#. Accounts'
@@ -5107,7 +5107,7 @@ msgstr "Grupa Artikla je način za klasifikaciju Artikala na temelju tipa."
#: erpnext/crm/doctype/appointment/appointment.py:74
msgid "An appointment booked through the portal can only be opened via email verification."
msgstr "Termin rezerviran putem portala može se otvoriti samo putem potvrde e-poštom."
msgstr ""
#. Description of the 'Notify by email on creation of automatic Material
#. Request' (Check) field in DocType 'Stock Settings'
@@ -5505,7 +5505,7 @@ msgstr "Imenovanje"
#. Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Appointment Booking Portal Settings"
msgstr "Postavke Portala za Zakazivanje Termina"
msgstr ""
#. Name of a DocType
#. Label of a Workspace Sidebar Item
@@ -5525,7 +5525,7 @@ msgstr "Potvrda Termina"
#: erpnext/crm/doctype/appointment/appointment.py:189
msgid "Appointment Confirmed"
msgstr "Termin Potvrđen"
msgstr ""
#: erpnext/www/book_appointment/index.js:237
msgid "Appointment Created Successfully"
@@ -5547,7 +5547,7 @@ msgstr "Trajanje Termina (u minutama)"
#. 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Appointment Scheduling"
msgstr "Zakazivanje Termina"
msgstr ""
#: erpnext/www/book_appointment/index.py:24
msgid "Appointment Scheduling Disabled"
@@ -5559,7 +5559,7 @@ msgstr "Zakazivanje termina je onemogućeno za ovu stranicu"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:101
msgid "Appointment Scheduling needs to be enabled for Appointment Booking through portal."
msgstr "Zakazivanje Termina mora biti omogućeno za Rezervaciju Termina putem portala."
msgstr ""
#. Label of the appointment_with (Link) field in DocType 'Appointment'
#: erpnext/crm/doctype/appointment/appointment.json
@@ -5568,31 +5568,31 @@ msgstr "Termin s"
#: erpnext/crm/doctype/appointment/appointment.py:86
msgid "Appointment can only be scheduled up to {0} day(s) in advance."
msgstr "Termin se može zakazati samo do {0} dana unaprijed."
msgstr ""
#: erpnext/crm/doctype/appointment/appointment.py:79
msgid "Appointment cannot be scheduled for a past time."
msgstr "Termin se ne može zakazati za prošlo vrijeme."
msgstr ""
#: erpnext/crm/doctype/appointment/appointment.py:98
msgid "Appointment cannot be scheduled on a holiday."
msgstr "Termin se ne može zakazati na praznik."
msgstr ""
#: erpnext/www/book_appointment/verify/index.py:28
msgid "Appointment has been closed. Please book the appointment again."
msgstr "Termin je zatvoren. Ponovo zakažete novi termin."
msgstr ""
#: erpnext/www/book_appointment/verify/index.py:33
msgid "Appointment is already verified."
msgstr "Termin je već potvrđen."
msgstr ""
#: erpnext/crm/doctype/appointment/appointment.py:116
msgid "Appointment must be scheduled within the available slot timings."
msgstr "Termin se mora zakazati unutar raspoloživih vremenskih utora."
msgstr ""
#: erpnext/crm/doctype/appointment/appointment.py:66
msgid "Appointments created manually cannot have 'Unverified' status."
msgstr "Ručno rezervirani termini ne mogu imati status 'Nepotvrđeno'."
msgstr ""
#. Label of the approving_role (Link) field in DocType 'Authorization Rule'
#: erpnext/setup/doctype/authorization_rule/authorization_rule.json
@@ -8649,7 +8649,7 @@ msgstr "Spremnik"
#: erpnext/stock/doctype/bin/bin.js:16
msgid "Bin Values Recalculated"
msgstr "Vrijednosti Spremnika Ponovo Izračunate"
msgstr ""
#. Label of the bio (Text Editor) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json
@@ -8784,7 +8784,7 @@ msgstr "Blokiraj Dostavljača"
#. 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
msgid "Block a new Sales Invoice when the customer's overdue amount exceeds the Overdue Limit set on the customer."
msgstr "Blokiraj novu Prodajnu Fakturu kada iznos dospjelog plaćanja klijenta premaši ograničenje dospjelog plaćanja postavljeno za klijenta."
msgstr ""
#. Description of the 'Is Frozen' (Check) field in DocType 'Customer'
#: erpnext/selling/doctype/customer/customer.json
@@ -9868,7 +9868,7 @@ msgstr "Sastavnica se nemože deaktivirati ili otkazati jer je povezana sa drugi
#: erpnext/crm/doctype/opportunity/opportunity.py:282
msgid "Cannot declare as Lost because an active Quotation exists."
msgstr "Ne može se proglasiti izgubljeno jer postoji aktivna Ponuda."
msgstr ""
#: erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.js:16
#: erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.js:26
@@ -9977,7 +9977,7 @@ msgstr "Ne može se upućivati na broj reda veći ili jednak trenutnom broju red
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:96
msgid "Cannot repost more than {0} vouchers at once. Split them into multiple documents."
msgstr "Nije moguće ponovo knjižiti više od {0} verifikata odjednom. Podijeli ih u više dokumenata."
msgstr ""
#: erpnext/accounts/doctype/bank/bank.js:63
msgid "Cannot retrieve link token for update. Check Error Log for more information"
@@ -13863,7 +13863,7 @@ msgstr "Izrađeno Migracijom"
#. Label of the created_through_portal (Check) field in DocType 'Appointment'
#: erpnext/crm/doctype/appointment/appointment.json
msgid "Created through Portal"
msgstr "Izrađeno putem Portala"
msgstr ""
#: erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py:251
msgid "Created {0} scorecards for {1} between:"
@@ -16439,7 +16439,7 @@ msgstr "Obriši Potencijalne Klijente i Adrese"
#. in DocType 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Delete Permanently"
msgstr "Trajno Izbriši"
msgstr ""
#. Label of the delete_transactions_status (Select) field in DocType
#. 'Transaction Deletion Record'
@@ -18421,7 +18421,7 @@ msgstr "Kopiraj red {0} sa istim {1}"
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:110
msgid "Duplicate vouchers found. Remove the duplicate vouchers to continue to repost."
msgstr "Pronađeni su duplikati verifikata. Ukloni duplikate verifikata da biste nastavili s ponovnim knjiženjem."
msgstr ""
#: erpnext/accounts/doctype/pricing_rule/pricing_rule.py:157
msgid "Duplicate {0} found in the table"
@@ -18742,11 +18742,11 @@ msgstr "E-pošta poslana Dobavljaču {0}"
#. Label of the email_verified (Check) field in DocType 'Appointment'
#: erpnext/crm/doctype/appointment/appointment.json
msgid "Email Verified"
msgstr "E-pošta Potvrđena"
msgstr ""
#: erpnext/accounts/doctype/payment_request/payment_request.js:57
msgid "Email couldn't be sent."
msgstr "E-pošta nije mogla biti poslana."
msgstr ""
#: erpnext/setup/doctype/employee/employee.py:440
msgid "Email is required to create a user"
@@ -18994,7 +18994,7 @@ msgstr "Omogući Dozvoli Djelomičnu Rezervaciju u Postavkama Zaliha da rezervi
#. Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Enable Appointment Booking Through Portal"
msgstr "Omogući Zakazivanje Termina Putem Portala"
msgstr ""
#. Label of the enable_scheduling (Check) field in DocType 'Appointment Booking
#. Settings'
@@ -23386,7 +23386,7 @@ msgstr "Lista Praznika"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:89
msgid "Holiday List - {0} is not valid for current date."
msgstr "Popis Praznika - {0} nije valjan za trenutni datum."
msgstr ""
#. Label of the holiday_list_name (Data) field in DocType 'Holiday List'
#: erpnext/setup/doctype/holiday_list/holiday_list.json
@@ -24361,7 +24361,7 @@ msgstr "U Minutama"
#. DocType 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "In Minutes (min: 15 mins, max: 60 mins)"
msgstr "U minutama (min: 15 min, maks: 60 min)"
msgstr ""
#: erpnext/accounts/report/accounts_payable/accounts_payable.js:149
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:181
@@ -28024,7 +28024,7 @@ msgstr "Artikal {0} nemože se dodati kao sam podsklop"
#: erpnext/stock/doctype/material_request/material_request.py:694
msgid "Item {0} cannot be ordered more than once"
msgstr "Artikal {0} se ne može naručiti više od jednom"
msgstr ""
#: erpnext/manufacturing/doctype/blanket_order/blanket_order.py:197
msgid "Item {0} cannot be ordered more than {1} against Blanket Order {2}."
@@ -30466,7 +30466,7 @@ msgstr "Označi kao Zatvoreno"
#. in DocType 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Mark as Closed"
msgstr "Odaberi kao Zatvoreno"
msgstr ""
#. Description of the 'Is Internal Customer' (Check) field in DocType
#. 'Customer'
@@ -32423,7 +32423,7 @@ msgstr "Nova Prodajna Faktura"
#. Credit Limit'
#: erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json
msgid "New Sales Invoices are blocked when the customer's overdue amount exceeds this. Requires 'Restrict Customer Over Billing' in Accounts Settings."
msgstr "Nove prodajne fakture se blokiraju kada iznos dospjelog duga klijenta premaši ovaj iznos. Zahtijeva opciju 'Ograniči Prekomjerno Fakturisanje Klijenta' u Postavkama Knjiženja."
msgstr ""
#. Label of the sales_order (Check) field in DocType 'Email Digest'
#: erpnext/setup/doctype/email_digest/email_digest.json
@@ -32680,7 +32680,7 @@ msgstr "Nema dostupnih dodatnih polja"
#: erpnext/crm/doctype/appointment/appointment.py:103
msgid "No availability of slots are found. Please add on Appointment Booking Settings."
msgstr "Nije pronađeno nikakvo slobodno vrijeme termina. Dodaj ih u Postavkama Zakazivanja Termina."
msgstr ""
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1367
msgid "No available quantity to reserve for item {0} in warehouse {1}"
@@ -34859,15 +34859,15 @@ msgstr "Dana Zakašnjenja"
#. Credit Limit'
#: erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json
msgid "Overdue Limit"
msgstr "Granica Dospijeća"
msgstr ""
#: erpnext/selling/doctype/customer/customer.py:707
msgid "Overdue Limit Crossed"
msgstr "Granica Dospijeća Prekoračena"
msgstr ""
#: erpnext/selling/doctype/customer/customer.py:702
msgid "Overdue Limit crossed for customer {0}. Overdue amount {1} exceeds the allowed limit {2}."
msgstr "Granica Dospijeća prekoračena je za {0}. Iznos dospijeća {1} prelazi dozvoljenu granicu {2}."
msgstr ""
#. Name of a DocType
#: erpnext/accounts/doctype/overdue_payment/overdue_payment.json
@@ -35798,7 +35798,7 @@ msgstr "Djelimično Usaglašeno"
#. Option for the 'Status' (Select) field in DocType 'Repost Accounting Ledger'
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json
msgid "Partially Reposted"
msgstr "Djelomično Ponovo Knjiženo"
msgstr ""
#. Option for the 'Status' (Select) field in DocType 'Stock Reservation Entry'
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
@@ -36562,7 +36562,7 @@ msgstr "Ograničenje Plaćanja"
#: erpnext/accounts/doctype/payment_request/payment_request.py:434
msgid "Payment Link couldn't be sent."
msgstr "Poveznica za plaćanje nije mogla biti poslana."
msgstr ""
#: erpnext/accounts/report/pos_register/pos_register.js:50
#: erpnext/accounts/report/pos_register/pos_register.py:126
@@ -37884,7 +37884,7 @@ msgstr "Dodaj Račun za Privremeno Otvaranje u Kontni Plan"
#: erpnext/crm/doctype/appointment/appointment.py:95
msgid "Please add a valid Holiday List on Appointment Booking Settings."
msgstr "Dodaj valjani Popis Praznika u Postavke Zakazivanja Termina."
msgstr ""
#: erpnext/accounts/doctype/bank_transaction_rule/bank_transaction_rule.py:119
msgid "Please add an account for the Bank Entry rule."
@@ -38269,7 +38269,7 @@ msgstr "Popuni Tabelu Prodajnih Naloga"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:57
msgid "Please fill up the Availability of Slots table to enable Appointment Scheduling."
msgstr "Popuni tablicu Dostupnosti Termina kako biste omogućili Zakazivanje Termina."
msgstr ""
#: erpnext/stock/doctype/shipment/shipment.js:277
msgid "Please first set Full Name, Email and Phone for the user"
@@ -38513,7 +38513,7 @@ msgstr "Odaberi Dostavnicu"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:81
msgid "Please select a Holiday List to enable Appointment Scheduling."
msgstr "Odaberi Popis Praznika kako biste omogućili Zakazivanje Termina."
msgstr ""
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py:153
msgid "Please select a Subcontracting Purchase Order."
@@ -38594,7 +38594,7 @@ msgstr "Odaberi važeći Nalog Nabave koji je konfigurisan za Podugovor."
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1355
msgid "Please select a valid {0}"
msgstr "Odaberi valjani {0}"
msgstr ""
#: erpnext/selling/doctype/quotation/quotation.js:245
msgid "Please select a value for {0} quotation_to {1}"
@@ -42540,12 +42540,12 @@ msgstr "Količina ne može biti veća od {0} za artikal {1}"
#: erpnext/stock/doctype/material_request/material_request.py:704
msgid "Quantity for Item {0} must be greater than zero and cannot exceed {1}"
msgstr "Količina za Artikal {0} mora biti veća od nule i ne može biti veća od {1}"
msgstr ""
#: erpnext/stock/doctype/material_request/material_request.js:564
msgctxt "<b>${pending_qty}</b>"
msgid "Quantity for Item {0} must be greater than zero and cannot exceed {1}"
msgstr "Količina za Artikal {0} mora biti veća od nule i ne može biti veća od {1}"
msgstr ""
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:564
msgid "Quantity is mandatory for the selected items."
@@ -43406,7 +43406,7 @@ msgstr "Ponovo izračunaj Stopu Vrednovanja"
#: erpnext/stock/doctype/bin/bin.js:10
msgid "Recalculate Values"
msgstr "Preračunaj Vrijednosti"
msgstr ""
#. Option for the 'Status' (Select) field in DocType 'Asset'
#. Option for the 'Purpose' (Select) field in DocType 'Asset Movement'
@@ -44608,7 +44608,7 @@ msgstr "Ponovno Knjiženje je započeto u pozadini"
#. Items'
#: erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json
msgid "Reposted"
msgstr "Ponovno Knjiženo"
msgstr ""
#. Label of the reposting_data_file (Attach) field in DocType 'Repost Item
#. Valuation'
@@ -44636,7 +44636,7 @@ msgstr "Referansa Ponovnog knjiženja"
#. 'Repost Accounting Ledger Items'
#: erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json
msgid "Reposting Status"
msgstr "Status Ponovnog Knjiženja"
msgstr ""
#. Label of the vouchers_based_on_item_and_warehouse_section (Section Break)
#. field in DocType 'Repost Item Valuation'
@@ -44650,11 +44650,11 @@ msgstr "Napred Ponovnog Knjiženja Kaučera"
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:216
msgid "Reposting can be started only for submitted document."
msgstr "Ponovno Knjiženje se može pokrenuti samo za podnešeni dokument."
msgstr ""
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:221
msgid "Reposting cannot be started when status is {0}."
msgstr "Ponovno Knjiženje se ne može pokrenuti kada je status {0}."
msgstr ""
#: erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py:227
#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:338
@@ -44679,11 +44679,11 @@ msgstr "Ponovno Knjiženje u pozadini."
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:211
msgid "Reposting is still in progress in background."
msgstr "Ponovno knjiženje je još uvijek u tijeku u pozadini."
msgstr ""
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:315
msgid "Reposting {0} {1}"
msgstr "Ponovno knjiženje {0} {1}"
msgstr ""
#. Label of the represents_company (Link) field in DocType 'Purchase Invoice'
#. Label of the represents_company (Link) field in DocType 'Sales Invoice'
@@ -45356,7 +45356,7 @@ msgstr "Ograniči"
#. 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
msgid "Restrict Customer Over Billing"
msgstr "Ograničiti Prekomjerno Fakturisanje Klijenta"
msgstr ""
#. Label of the restrict_based_on (Select) field in DocType 'Party Specific
#. Item'
@@ -45678,7 +45678,7 @@ msgstr "Obrnuta Signatura"
#: erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py:628
msgid "Reverse {0} already available in draft status: {1}"
msgstr "Obrnuto {0} već je dostupno u statusu nacrta: {1}"
msgstr ""
#: erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js:118
msgid "Reversing Journals..."
@@ -45807,7 +45807,7 @@ msgstr "Štap"
#. 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
msgid "Role Allowed to Bypass Over Billing Restriction"
msgstr "Uloga kojoj je dopušteno zaobilaženje Ograničenja Prekomjernog Fakturisanja"
msgstr ""
#. Label of the role_allowed_to_over_deliver_receive (Link) field in DocType
#. 'Stock Settings'
@@ -48518,7 +48518,7 @@ msgstr "Raspoređivač je neaktivan. Nije moguće spojiti račune."
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:232
msgid "Scheduler is inactive. Reposting will only run once background jobs are processed."
msgstr "Zakazivač je neaktivan. Ponovno Knjiženje će se pokrenuti tek nakon što se obrade pozadinski zadaci."
msgstr ""
#. Label of the schedules (Table) field in DocType 'Maintenance Schedule'
#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json
@@ -48951,7 +48951,7 @@ msgstr "Odaberi Adresu Dobavljača"
#: erpnext/stock/doctype/material_request/material_request.js:448
msgid "Select Supplier for Items"
msgstr "Odaberi Dobavljača za Artikle"
msgstr ""
#: erpnext/stock/doctype/batch/batch.js:150
msgid "Select Target Warehouse"
@@ -49005,7 +49005,7 @@ msgstr "Odaberi Dobavljača"
#: erpnext/stock/doctype/material_request/material_request.js:552
#: erpnext/stock/doctype/material_request/material_request.py:699
msgid "Select a Supplier for Item {0}"
msgstr "Odaberi Dobavljača za Artikal {0}"
msgstr ""
#: banking/src/components/features/BankReconciliation/MatchAndReconcile.tsx:49
msgid "Select a bank account to reconcile"
@@ -49046,7 +49046,7 @@ msgstr "Odaber artikal iz svakog skupa koja će se koristiti u Prodajnom Nalogu.
#: erpnext/stock/doctype/material_request/material_request.js:539
#: erpnext/stock/doctype/material_request/material_request.py:680
msgid "Select at least one Item"
msgstr "Odaberi barem jedan Artikal"
msgstr ""
#: erpnext/stock/doctype/item/item.js:944
msgid "Select at least one attribute value."
@@ -49365,7 +49365,7 @@ msgstr "Pošalji sa Prilogom"
#: erpnext/accounts/doctype/payment_request/payment_request.js:51
#: erpnext/accounts/doctype/payment_request/payment_request.js:55
msgid "Sending Email"
msgstr "Slanje e-pošte u tijeku"
msgstr ""
#. Option for the 'Detected Amount Format' (Select) field in DocType 'Bank
#. Statement Import Log'
@@ -50194,7 +50194,7 @@ msgstr "Postavi Dobavljača"
#: erpnext/stock/doctype/material_request/material_request.js:455
msgid "Set Supplier for All Items"
msgstr "Postavi Dobavljača za Sve Artikle"
msgstr ""
#. Label of the set_target_warehouse (Link) field in DocType 'Sales Invoice'
#. Label of the set_warehouse (Link) field in DocType 'Purchase Order'
@@ -52609,7 +52609,7 @@ msgstr "Zaliha nije dostupna za Artikal {0} u Skladištu {1}."
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1240
msgid "Stock not available to reserve for the Item {0} in Warehouse {1}."
msgstr "Zaliha nije dostupna za rezervaciju za Artikal {0} u Skladištu {1}."
msgstr ""
#: erpnext/selling/page/point_of_sale/pos_controller.js:826
msgid "Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2} {3}."
@@ -55286,7 +55286,7 @@ msgstr "Računa pod Obavezama ili Kapitalom, u kojoj će se knjižiti Rezultat"
#: erpnext/accounts/doctype/account/account.py:222
msgid "The account type of {0} cannot be changed from {1} because stock ledger entries exist against it."
msgstr "Tip računa {0} ne može se promijeniti iz {1} jer postoje unosi u Registru Zaliha."
msgstr ""
#: erpnext/accounts/doctype/payment_request/payment_request.py:1016
msgid "The allocated amount is greater than the outstanding amount of Payment Request {0}"
@@ -55444,7 +55444,7 @@ msgstr "Sljedeći redovi su duplikati:"
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:130
msgid "The following vouchers are not submitted: {0}"
msgstr "Sljedeći verifikati nisu podnešeni: {0}"
msgstr ""
#: erpnext/stock/doctype/material_request/material_request.py:1062
msgid "The following {0} were created: {1}"
@@ -55956,7 +55956,7 @@ msgstr "Ovaj dokument je preko ograničenja za {0} {1} za artikal {4}. Da li pra
#: erpnext/templates/emails/appointment_confirmed.html:6
msgid "This email was sent from {0}"
msgstr "Ova e-pošta je poslana od {0}"
msgstr ""
#: erpnext/stock/doctype/delivery_note/delivery_note.js:496
msgid "This field is used to set the 'Customer'."
@@ -56102,7 +56102,7 @@ msgstr "Ovaj filter artikala je već primijenjen za {0}"
#: erpnext/templates/emails/confirm_appointment.html:4
msgid "This link is valid for {0} minutes"
msgstr "Ova poveznica vrijedi {0} minuta"
msgstr ""
#: erpnext/www/banking.py:35
msgid "This method is only meant for developer mode"
@@ -56223,7 +56223,7 @@ msgstr "Ova vrijednost će se koristiti kada se ne pronađe odgovarajući Zajedn
#: erpnext/www/book_appointment/verify/index.py:18
msgid "This verification link is invalid. Please book the appointment again."
msgstr "Ova poveznica za verifikaciju je nevažeća. Ponovo zakaži termin."
msgstr ""
#: banking/src/components/features/Settings/Preferences.tsx:86
msgid "This will automatically run transaction matching rules on unreconciled transactions every hour."
@@ -58030,7 +58030,7 @@ msgstr "Transakcije naspram Tvrtke već postoje! Kontni Plan se može uvesti sam
#. 'Customer'
#: erpnext/selling/doctype/customer/customer.json
msgid "Transactions are blocked when the outstanding balance exceeds the credit limit. When Restrict Customer Over Billing is enabled, new invoices are also blocked when the customer's overdue amount exceeds the Overdue Limit."
msgstr "Transakcije se blokiraju kada preostali dug premaši kreditnu granicu. Kada je omogućena opcija Ograniči Prekomjerno Fakturisanja Klijenta, nove fakture se također blokiraju kada iznos dospjelih obaveza klijenta premaši granicu za dospjele obaveze."
msgstr ""
#: banking/src/components/features/BankStatementImporter/CSV/StatementDetails.tsx:239
msgid "Transactions to be imported into the system"
@@ -58651,7 +58651,7 @@ msgstr "Poništi Dodjele"
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:375
msgid "Unable to Repost Accounting Ledger"
msgstr "Ponovo knjiži Knjigovodstveni Registar"
msgstr ""
#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:477
msgid "Unable to fetch DocType details. Please contact system administrator."
@@ -59594,7 +59594,7 @@ msgstr "Korisnicima sa ovom ulogom je dozvoljena prekomjerna Dostava/Primanje na
#. field in DocType 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
msgid "Users with this role can still submit invoices for customers who have crossed their Overdue Limit."
msgstr "Korisnici s ovom ulogom i dalje mogu podnositi fakture za klijente koji su prekoračili granicu dospjelosti."
msgstr ""
#. Description of the 'Role to Notify on Depreciation Failure' (Link) field in
#. DocType 'Accounts Settings'
@@ -60141,12 +60141,12 @@ msgstr "Rizični Kapital"
#. 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Verification Link Expiry Duration"
msgstr "Trajanje Vađenaj Verifikacijske Poveznice"
msgstr ""
#. Label of the verification_token (Data) field in DocType 'Appointment'
#: erpnext/crm/doctype/appointment/appointment.json
msgid "Verification Token"
msgstr "Verifikacijski Kod"
msgstr ""
#: erpnext/www/book_appointment/verify/index.html:15
msgid "Verification failed please check the link"
@@ -60154,7 +60154,7 @@ msgstr "Verifikacija nije uspjela, provjeri vezu"
#: erpnext/www/book_appointment/verify/index.py:38
msgid "Verification link has expired."
msgstr "Veza za provjeru je istekla."
msgstr ""
#. Label of the verified_by (Data) field in DocType 'Quality Inspection'
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json
@@ -61012,7 +61012,7 @@ msgstr "Vidimo da je {0} napravljen protiv {1}. Ako želite da se ažuriraju pre
#: erpnext/templates/emails/appointment_confirmed.html:3
msgid "We look forward to meeting you"
msgstr "Radujemo se susretu s vama"
msgstr ""
#: banking/src/pages/BankStatementImporter.tsx:169
msgid "We support uploading CSV, XLSX, XLS and PDF files. Please make sure the file contains the correct columns."
@@ -62123,7 +62123,7 @@ msgstr "Vaše Ime (obavezno)"
#: erpnext/templates/emails/appointment_confirmed.html:2
msgid "Your email has been verified and your appointment has been confirmed for {0}"
msgstr "Vaša e-pošta je potvrđena i vaš termin je potvrđen za {0}"
msgstr ""
#: erpnext/www/book_appointment/verify/index.html:11
msgid "Your email has been verified and your appointment has been scheduled"
@@ -62948,7 +62948,7 @@ msgstr "Prikaz {0} trenutno nije podržan u Prilagođenom Financijskom Izvješć
#: erpnext/stock/doctype/material_request/material_request.py:732
msgid "{0} was set to today for items whose requested date has passed"
msgstr "{0} je postavljen na danas za artikle čiji je traženi datum prošao"
msgstr ""
#: erpnext/accounts/doctype/payment_term/payment_term.js:19
msgid "{0} will be given as discount."

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2026-08-02 10:09+0000\n"
"PO-Revision-Date: 2026-08-06 10:01\n"
"PO-Revision-Date: 2026-08-04 09:43\n"
"Last-Translator: hello@frappe.io\n"
"Language-Team: Swedish\n"
"MIME-Version: 1.0\n"
@@ -152,7 +152,7 @@ msgstr "% Klart Sätt"
#: erpnext/projects/doctype/project/project.py:226
msgid "% Complete must be between 0 and 100"
msgstr "% Färdig måste vara mellan 0 och 100"
msgstr ""
#. Label of the percent_complete (Percent) field in DocType 'Project'
#: erpnext/projects/doctype/project/project.json
@@ -349,7 +349,7 @@ msgstr "\"Uppdatera Lager\" kan inte väljas för Fast Tillgång Försäljning"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:112
msgid "'Verification Link Expiry Duration' must be between 15 to 60 minutes."
msgstr "'Verifiering Länk Utgång Tid' måste vara mellan 15 och 60 minuter."
msgstr ""
#: erpnext/accounts/doctype/bank_account/bank_account.py:79
msgid "'{0}' account is already used by {1}. Use another account."
@@ -1125,7 +1125,7 @@ msgstr "Inaktiverad Artikel Paket kan inte väljas i transaktioner."
#: erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py:636
msgid "A draft reverse journal for {0} has been created: {1}"
msgstr "Utkast till omvänd journal för {0} har skapats: {1}"
msgstr ""
#: erpnext/stock/doctype/delivery_trip/delivery_trip.py:59
msgid "A driver must be set to submit."
@@ -1170,7 +1170,7 @@ msgstr "Kvalitet kontroll måste genomföras innan Inköp Följesedel skapas fö
#: erpnext/stock/doctype/material_request/material_request.js:476
msgid "A separate Purchase Order is created for each Supplier."
msgstr "Separat Inköp Order skapas för varje Leverantör."
msgstr ""
#: erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py:96
msgid "A template with tax category {0} already exists. Only one template is allowed with each tax category"
@@ -1183,7 +1183,7 @@ msgstr "Tredje parts distributör / handlare / kommissionär / återförsäljare
#: erpnext/crm/doctype/appointment/appointment.py:70
msgid "A verified appointment cannot be moved back to 'Unverified' status."
msgstr "Verifierad bokning kan inte flyttas tillbaka till \"Overifierad\" status."
msgstr ""
#. Option for the 'Blood Group' (Select) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json
@@ -2374,7 +2374,7 @@ msgstr "Åtgärd Initierad"
#. DocType 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Action for Expired Unverified Appointments"
msgstr "Åtgärd för Utgångna, Overifierade Bokningar"
msgstr ""
#. Label of the action_if_accumulated_monthly_budget_exceeded (Select) field in
#. DocType 'Budget'
@@ -2920,7 +2920,7 @@ msgstr "Lägg till alla konton som du vill dela upp transaktion i."
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:92
msgid "Add atleast one voucher to repost."
msgstr "Lägg till minst ett verifikat för att bokföra om."
msgstr ""
#: erpnext/www/book_appointment/index.html:42
msgid "Add details"
@@ -3437,7 +3437,7 @@ msgstr "Förskott Belopp"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:93
msgid "Advance Booking Days is mandatory for Appointment Scheduling."
msgstr "Förhandsbokning erfordras för Tdsbokning Schemaläggning."
msgstr ""
#. Label of the advance_paid (Currency) field in DocType 'Sales Order'
#: erpnext/selling/doctype/sales_order/sales_order.json
@@ -3760,7 +3760,7 @@ msgstr "Ålder ({0})"
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:102
#: erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js:28
msgid "Age as on"
msgstr "Ålder per"
msgstr ""
#. Label of the ageing_based_on (Select) field in DocType 'Process Statement Of
#. Accounts'
@@ -5114,7 +5114,7 @@ msgstr "Artikel grupp är ett sätt att klassificera artiklar baserat på typer.
#: erpnext/crm/doctype/appointment/appointment.py:74
msgid "An appointment booked through the portal can only be opened via email verification."
msgstr "Bokad tid via portal kan endast öppnas via e-post verifiering."
msgstr ""
#. Description of the 'Notify by email on creation of automatic Material
#. Request' (Check) field in DocType 'Stock Settings'
@@ -5512,7 +5512,7 @@ msgstr "Möte"
#. Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Appointment Booking Portal Settings"
msgstr "Tid Bokning Portal Inställningar"
msgstr ""
#. Name of a DocType
#. Label of a Workspace Sidebar Item
@@ -5532,7 +5532,7 @@ msgstr "Tid Bokning Bekräftelse"
#: erpnext/crm/doctype/appointment/appointment.py:189
msgid "Appointment Confirmed"
msgstr "Tidsbokning Bekräftad"
msgstr ""
#: erpnext/www/book_appointment/index.js:237
msgid "Appointment Created Successfully"
@@ -5554,7 +5554,7 @@ msgstr "Tid Bokning Varar (Minuter)"
#. 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Appointment Scheduling"
msgstr "Tidsbokning Schemaläggning"
msgstr ""
#: erpnext/www/book_appointment/index.py:24
msgid "Appointment Scheduling Disabled"
@@ -5566,7 +5566,7 @@ msgstr "Tid Bokning är Inaktiverad för denna Webbplats"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:101
msgid "Appointment Scheduling needs to be enabled for Appointment Booking through portal."
msgstr "Tidsbokning Schemaläggning måste vara aktiverad för Tidsbokning via portal."
msgstr ""
#. Label of the appointment_with (Link) field in DocType 'Appointment'
#: erpnext/crm/doctype/appointment/appointment.json
@@ -5575,31 +5575,31 @@ msgstr "Tid Bokning med"
#: erpnext/crm/doctype/appointment/appointment.py:86
msgid "Appointment can only be scheduled up to {0} day(s) in advance."
msgstr "Tidsbokning kan endast schemaläggas upp till {0} dag(ar) i förväg."
msgstr ""
#: erpnext/crm/doctype/appointment/appointment.py:79
msgid "Appointment cannot be scheduled for a past time."
msgstr "Tidsbokning kan inte schemaläggas för förfluten tid."
msgstr ""
#: erpnext/crm/doctype/appointment/appointment.py:98
msgid "Appointment cannot be scheduled on a holiday."
msgstr "Tidsbokning kan inte schemaläggas på helgdag."
msgstr ""
#: erpnext/www/book_appointment/verify/index.py:28
msgid "Appointment has been closed. Please book the appointment again."
msgstr "Tidsbokning har stängts. Boka igen."
msgstr ""
#: erpnext/www/book_appointment/verify/index.py:33
msgid "Appointment is already verified."
msgstr "Tidsbokning är redan bekräftad."
msgstr ""
#: erpnext/crm/doctype/appointment/appointment.py:116
msgid "Appointment must be scheduled within the available slot timings."
msgstr "Tidsbokning måste schemaläggas inom tillgänglig tidsintervall."
msgstr ""
#: erpnext/crm/doctype/appointment/appointment.py:66
msgid "Appointments created manually cannot have 'Unverified' status."
msgstr "Tidsbokningar som skapas manuellt kan inte ha ”Overifierad” status."
msgstr ""
#. Label of the approving_role (Link) field in DocType 'Authorization Rule'
#: erpnext/setup/doctype/authorization_rule/authorization_rule.json
@@ -8791,7 +8791,7 @@ msgstr "Spärra Leverantör"
#. 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
msgid "Block a new Sales Invoice when the customer's overdue amount exceeds the Overdue Limit set on the customer."
msgstr "Spärra ny Försäljning Faktura när kundens förfallna belopp överstiger förfallen gräns angiven för kund."
msgstr ""
#. Description of the 'Is Frozen' (Check) field in DocType 'Customer'
#: erpnext/selling/doctype/customer/customer.json
@@ -9875,7 +9875,7 @@ msgstr "Kan inte inaktivera eller annullera Stycklista eftersom den är kopplat
#: erpnext/crm/doctype/opportunity/opportunity.py:282
msgid "Cannot declare as Lost because an active Quotation exists."
msgstr "Kan inte ange som förlorad eftersom det finns aktiv Offert."
msgstr ""
#: erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.js:16
#: erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.js:26
@@ -9984,7 +9984,7 @@ msgstr "Kan inte hänvisa till rad nummer högre än eller lika med aktuell rad
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:96
msgid "Cannot repost more than {0} vouchers at once. Split them into multiple documents."
msgstr "Kunde inte återbokföra fler än {0} verifikationer samtidigt. Dela upp dem i flera dokument."
msgstr ""
#: erpnext/accounts/doctype/bank/bank.js:63
msgid "Cannot retrieve link token for update. Check Error Log for more information"
@@ -13870,7 +13870,7 @@ msgstr "Skapad av Migrering"
#. Label of the created_through_portal (Check) field in DocType 'Appointment'
#: erpnext/crm/doctype/appointment/appointment.json
msgid "Created through Portal"
msgstr "Skapad via Portal"
msgstr ""
#: erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py:251
msgid "Created {0} scorecards for {1} between:"
@@ -16446,7 +16446,7 @@ msgstr "Ta bort Prospekt och Adresser"
#. in DocType 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Delete Permanently"
msgstr "Ta bort Permanent"
msgstr ""
#. Label of the delete_transactions_status (Select) field in DocType
#. 'Transaction Deletion Record'
@@ -18342,7 +18342,7 @@ msgstr "Påminnelse Typ"
#: erpnext/accounts/doctype/pos_profile/pos_profile.py:170
msgid "Duplicate Customer Group"
msgstr "Duplicera Kund Grupp"
msgstr "Kopiera Kund Grupp"
#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:190
msgid "Duplicate DocType"
@@ -18354,11 +18354,11 @@ msgstr "Dubblett Post. Kontrollera Auktorisering Regel {0}"
#: erpnext/assets/doctype/asset/asset.py:418
msgid "Duplicate Finance Book"
msgstr "Duplicera Bokslut Register"
msgstr "Kopiera Bokslut Register"
#: erpnext/accounts/doctype/pos_profile/pos_profile.py:164
msgid "Duplicate Item Group"
msgstr "Duplicera Artikel Grupp"
msgstr "Kopiera Artikel Grupp"
#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:102
msgid "Duplicate Item Under Same Parent"
@@ -18376,7 +18376,7 @@ msgstr "Duplicera Kassa Fällt"
#: erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py:104
#: erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py:64
msgid "Duplicate POS Invoices found"
msgstr "Dubblett av Kassa Fakturor hittad"
msgstr "Kopia av Kassa Fakturor hittad"
#: erpnext/accounts/doctype/payment_request/payment_request.py:134
msgid "Duplicate Payment Schedule selected"
@@ -18384,7 +18384,7 @@ msgstr "Duplicerad Betalning Schema vald"
#: erpnext/projects/doctype/project/project.js:83
msgid "Duplicate Project with Tasks"
msgstr "Duplicera Projekt med Uppgifter"
msgstr "Kopiera Projekt med Uppgifter"
#: erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py:157
msgid "Duplicate Sales Invoices found"
@@ -18404,7 +18404,7 @@ msgstr "Kopia av Kund Grupp finns i Kund Grupp Tabell"
#: erpnext/stock/doctype/item_manufacturer/item_manufacturer.py:44
msgid "Duplicate entry against the item code {0} and manufacturer {1}"
msgstr "Duplicera post mot artikel kod {0} och producent {1}"
msgstr "Kopiera post mot Artikel Kod {0} och Producent {1}"
#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:189
msgid "Duplicate entry: {0}{1}"
@@ -18412,27 +18412,27 @@ msgstr "Duplicerad post: {0}{1}"
#: erpnext/accounts/doctype/pos_profile/pos_profile.py:164
msgid "Duplicate item group found in the item group table"
msgstr "Dubblett av Artikel Grupp hittad i Artikel Grupp Tabell"
msgstr "Kopiera Artikel Grupp hittad i Artikel Grupp Tabell"
#: erpnext/accounts/doctype/dunning_type/dunning_type.py:133
msgid "Duplicate languages found on Dunning Letter Text. Keep only one of them."
msgstr "Det finns flera språk i Påminnelse Brev. Behåll endast ett språk."
msgstr "Det finns flera språk i påminnelse brev. Behåll endast ett språk."
#: erpnext/projects/doctype/project/project.js:186
msgid "Duplicate project has been created"
msgstr "Dubblett av Projekt är skapad"
msgstr "Kopia av Projekt är skapad"
#: erpnext/utilities/transaction_base.py:112
msgid "Duplicate row {0} with same {1}"
msgstr "Duplicera Rad {0} med samma {1}"
msgstr "Kopiera Rad {0} med samma {1}"
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:110
msgid "Duplicate vouchers found. Remove the duplicate vouchers to continue to repost."
msgstr "Dubbletter av verifikat hittades. Ta bort dubbletter för att fortsätta återbokföring."
msgstr ""
#: erpnext/accounts/doctype/pricing_rule/pricing_rule.py:157
msgid "Duplicate {0} found in the table"
msgstr "Dubblett {0} hittades i Tabell"
msgstr "Kopia {0} hittades i Tabell"
#. Label of the duration (Int) field in DocType 'Task'
#: erpnext/projects/doctype/task/task.json
@@ -18749,11 +18749,11 @@ msgstr "E-post Skickad till Leverantör {0}"
#. Label of the email_verified (Check) field in DocType 'Appointment'
#: erpnext/crm/doctype/appointment/appointment.json
msgid "Email Verified"
msgstr "E-post Verifierad"
msgstr ""
#: erpnext/accounts/doctype/payment_request/payment_request.js:57
msgid "Email couldn't be sent."
msgstr "E-post meddelande kunde inte skickas."
msgstr ""
#: erpnext/setup/doctype/employee/employee.py:440
msgid "Email is required to create a user"
@@ -19001,7 +19001,7 @@ msgstr "Aktivera Tillåt Partiell Reservation i Lager Inställningar för att re
#. Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Enable Appointment Booking Through Portal"
msgstr "Aktivera Tidsbokning via Portal"
msgstr ""
#. Label of the enable_scheduling (Check) field in DocType 'Appointment Booking
#. Settings'
@@ -23392,7 +23392,7 @@ msgstr "Helg Lista"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:89
msgid "Holiday List - {0} is not valid for current date."
msgstr "Helgdag Lista - {0} är inte giltig för aktuellt datum."
msgstr ""
#. Label of the holiday_list_name (Data) field in DocType 'Holiday List'
#: erpnext/setup/doctype/holiday_list/holiday_list.json
@@ -24367,7 +24367,7 @@ msgstr "I Minuter"
#. DocType 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "In Minutes (min: 15 mins, max: 60 mins)"
msgstr "I Minuter (min: 15 min, max: 60 min)"
msgstr ""
#: erpnext/accounts/report/accounts_payable/accounts_payable.js:149
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.js:181
@@ -28030,7 +28030,7 @@ msgstr "Artikel {0} kan inte läggas till som underenhet av sig själv"
#: erpnext/stock/doctype/material_request/material_request.py:694
msgid "Item {0} cannot be ordered more than once"
msgstr "Artikel {0} kan inte skapas order för mer än en gång"
msgstr ""
#: erpnext/manufacturing/doctype/blanket_order/blanket_order.py:197
msgid "Item {0} cannot be ordered more than {1} against Blanket Order {2}."
@@ -30472,7 +30472,7 @@ msgstr "Ange som Stängd "
#. in DocType 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Mark as Closed"
msgstr "Ange som Stängd"
msgstr ""
#. Description of the 'Is Internal Customer' (Check) field in DocType
#. 'Customer'
@@ -32429,7 +32429,7 @@ msgstr "Ny Försäljning Faktura"
#. Credit Limit'
#: erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json
msgid "New Sales Invoices are blocked when the customer's overdue amount exceeds this. Requires 'Restrict Customer Over Billing' in Accounts Settings."
msgstr "Nya Försäljning Fakturor spärras när kundens förfallna belopp överstiger detta belopp. Erfordrar att alternativ ”Begränsa Kund Överfakturering” är aktiverad i Bokföring Inställningar."
msgstr ""
#. Label of the sales_order (Check) field in DocType 'Email Digest'
#: erpnext/setup/doctype/email_digest/email_digest.json
@@ -32686,7 +32686,7 @@ msgstr "Inga extra fält tillgängliga"
#: erpnext/crm/doctype/appointment/appointment.py:103
msgid "No availability of slots are found. Please add on Appointment Booking Settings."
msgstr "Inga lediga tider hittades. Lägg till detta i Tidsbokning Inställningar."
msgstr ""
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1367
msgid "No available quantity to reserve for item {0} in warehouse {1}"
@@ -34865,15 +34865,15 @@ msgstr "Försening Dagar"
#. Credit Limit'
#: erpnext/selling/doctype/customer_credit_limit/customer_credit_limit.json
msgid "Overdue Limit"
msgstr "Förfallen Gräns"
msgstr ""
#: erpnext/selling/doctype/customer/customer.py:707
msgid "Overdue Limit Crossed"
msgstr "Förfallen Gräns Överskriden"
msgstr ""
#: erpnext/selling/doctype/customer/customer.py:702
msgid "Overdue Limit crossed for customer {0}. Overdue amount {1} exceeds the allowed limit {2}."
msgstr "Förfallen Gräns överskriden för kund {0}. Förfallen belopp {1} överskrider tillåten gräns {2}."
msgstr ""
#. Name of a DocType
#: erpnext/accounts/doctype/overdue_payment/overdue_payment.json
@@ -35804,7 +35804,7 @@ msgstr "Delvis Avstämd"
#. Option for the 'Status' (Select) field in DocType 'Repost Accounting Ledger'
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json
msgid "Partially Reposted"
msgstr "Delvis Återbokförd"
msgstr ""
#. Option for the 'Status' (Select) field in DocType 'Stock Reservation Entry'
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.json
@@ -36568,7 +36568,7 @@ msgstr "Betalning Gräns"
#: erpnext/accounts/doctype/payment_request/payment_request.py:434
msgid "Payment Link couldn't be sent."
msgstr "Betalning Länk kunde inte skickas."
msgstr ""
#: erpnext/accounts/report/pos_register/pos_register.js:50
#: erpnext/accounts/report/pos_register/pos_register.py:126
@@ -37890,7 +37890,7 @@ msgstr "Lägg till Tillfällig Öppning Konto i Kontoplan"
#: erpnext/crm/doctype/appointment/appointment.py:95
msgid "Please add a valid Holiday List on Appointment Booking Settings."
msgstr "Lägg till giltig Helgdag Lista i Tidsbokning Inställningar."
msgstr ""
#: erpnext/accounts/doctype/bank_transaction_rule/bank_transaction_rule.py:119
msgid "Please add an account for the Bank Entry rule."
@@ -38275,7 +38275,7 @@ msgstr "Fyll i Försäljning Order Tabell"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:57
msgid "Please fill up the Availability of Slots table to enable Appointment Scheduling."
msgstr "Fyll i tabell ”Lediga Tider” för att aktivera Tidsbokning Schemaläggning."
msgstr ""
#: erpnext/stock/doctype/shipment/shipment.js:277
msgid "Please first set Full Name, Email and Phone for the user"
@@ -38519,7 +38519,7 @@ msgstr "Välj Försäljning Följesedel"
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py:81
msgid "Please select a Holiday List to enable Appointment Scheduling."
msgstr "Välj Helgdag Lista för att aktivera Tidsbokning Schemaläggning."
msgstr ""
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py:153
msgid "Please select a Subcontracting Purchase Order."
@@ -38600,7 +38600,7 @@ msgstr "Välj giltig Inköp Order som är konfigurerad för Underleverantör."
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1355
msgid "Please select a valid {0}"
msgstr "Välj giltig {0}"
msgstr ""
#: erpnext/selling/doctype/quotation/quotation.js:245
msgid "Please select a value for {0} quotation_to {1}"
@@ -42546,12 +42546,12 @@ msgstr "Kvantitet kan inte vara högre än {0} för artikel {1}"
#: erpnext/stock/doctype/material_request/material_request.py:704
msgid "Quantity for Item {0} must be greater than zero and cannot exceed {1}"
msgstr "Kvantitet för artikel {0} måste vara högre än noll och får inte överstiga {1}"
msgstr ""
#: erpnext/stock/doctype/material_request/material_request.js:564
msgctxt "<b>${pending_qty}</b>"
msgid "Quantity for Item {0} must be greater than zero and cannot exceed {1}"
msgstr "Kvantitet för artikel {0} måste vara högre än noll och får inte överstiga {1}"
msgstr ""
#: erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js:564
msgid "Quantity is mandatory for the selected items."
@@ -43412,7 +43412,7 @@ msgstr "Räkna om Värdering Pris"
#: erpnext/stock/doctype/bin/bin.js:10
msgid "Recalculate Values"
msgstr "Beräkna om Värden"
msgstr ""
#. Option for the 'Status' (Select) field in DocType 'Asset'
#. Option for the 'Purpose' (Select) field in DocType 'Asset Movement'
@@ -44614,7 +44614,7 @@ msgstr "Bokföring startad i bakgrunden"
#. Items'
#: erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json
msgid "Reposted"
msgstr "Återbokförd"
msgstr ""
#. Label of the reposting_data_file (Attach) field in DocType 'Repost Item
#. Valuation'
@@ -44642,7 +44642,7 @@ msgstr "Ombokning Referens"
#. 'Repost Accounting Ledger Items'
#: erpnext/accounts/doctype/repost_accounting_ledger_items/repost_accounting_ledger_items.json
msgid "Reposting Status"
msgstr "Återbokförd Status"
msgstr ""
#. Label of the vouchers_based_on_item_and_warehouse_section (Section Break)
#. field in DocType 'Repost Item Valuation'
@@ -44656,11 +44656,11 @@ msgstr "Ombokning av Verifikat Framsteg"
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:216
msgid "Reposting can be started only for submitted document."
msgstr "Återbokföring kan endast påbörjas för godkända dokument."
msgstr ""
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:221
msgid "Reposting cannot be started when status is {0}."
msgstr "Återbokföring kan inte påbörjas när status är {0}."
msgstr ""
#: erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py:227
#: erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py:338
@@ -44685,11 +44685,11 @@ msgstr "Ombokning i bakgrund."
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:211
msgid "Reposting is still in progress in background."
msgstr "Återbokföring pågår fortfarande i bakgrunden."
msgstr ""
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:315
msgid "Reposting {0} {1}"
msgstr "Återbokför {0} {1}"
msgstr ""
#. Label of the represents_company (Link) field in DocType 'Purchase Invoice'
#. Label of the represents_company (Link) field in DocType 'Sales Invoice'
@@ -45362,7 +45362,7 @@ msgstr "Begränsa"
#. 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
msgid "Restrict Customer Over Billing"
msgstr "Begränsa Kund Överfakturering"
msgstr ""
#. Label of the restrict_based_on (Select) field in DocType 'Party Specific
#. Item'
@@ -45684,7 +45684,7 @@ msgstr "Omvänd Signatur"
#: erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py:628
msgid "Reverse {0} already available in draft status: {1}"
msgstr "Omvänd {0} finns redan tillgänglig som utkast: {1}"
msgstr ""
#: erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js:118
msgid "Reversing Journals..."
@@ -45813,7 +45813,7 @@ msgstr "Stav"
#. 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
msgid "Role Allowed to Bypass Over Billing Restriction"
msgstr "Roll Tillåten att Kringgå Överfakturering Begränsning"
msgstr ""
#. Label of the role_allowed_to_over_deliver_receive (Link) field in DocType
#. 'Stock Settings'
@@ -46320,7 +46320,7 @@ msgstr "Rad # #{0}: Avskrivning Start Datum erfordras"
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:336
msgid "Row #{0}: Duplicate entry in References {1} {2}"
msgstr "Rad #{0}: Dubblett Post i Referenser {1} {2}"
msgstr "Rad # {0}: Duplikat Post i Referenser {1} {2}"
#: erpnext/selling/doctype/sales_order/sales_order.py:332
msgid "Row #{0}: Expected Delivery Date cannot be before Purchase Order Date"
@@ -48525,7 +48525,7 @@ msgstr "Schemaläggare är inaktiv. Kan inte slå samman konton."
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:232
msgid "Scheduler is inactive. Reposting will only run once background jobs are processed."
msgstr "Schemaläggare är inaktiv. Återbokföring kommer endast att köras när bakgrundsjobb är klara."
msgstr ""
#. Label of the schedules (Table) field in DocType 'Maintenance Schedule'
#: erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json
@@ -48958,7 +48958,7 @@ msgstr "Välj Leverantör Adress"
#: erpnext/stock/doctype/material_request/material_request.js:448
msgid "Select Supplier for Items"
msgstr "Välj Leverantör för Artiklar"
msgstr ""
#: erpnext/stock/doctype/batch/batch.js:150
msgid "Select Target Warehouse"
@@ -49012,7 +49012,7 @@ msgstr "Välj Leverantör"
#: erpnext/stock/doctype/material_request/material_request.js:552
#: erpnext/stock/doctype/material_request/material_request.py:699
msgid "Select a Supplier for Item {0}"
msgstr "Välj Leverantör för Artikel {0}"
msgstr ""
#: banking/src/components/features/BankReconciliation/MatchAndReconcile.tsx:49
msgid "Select a bank account to reconcile"
@@ -49053,7 +49053,7 @@ msgstr "Välj artikel från varje uppsättning som ska användas i Försäljning
#: erpnext/stock/doctype/material_request/material_request.js:539
#: erpnext/stock/doctype/material_request/material_request.py:680
msgid "Select at least one Item"
msgstr "Välj minst en artikel"
msgstr ""
#: erpnext/stock/doctype/item/item.js:944
msgid "Select at least one attribute value."
@@ -49372,7 +49372,7 @@ msgstr "Skicka med Bilaga"
#: erpnext/accounts/doctype/payment_request/payment_request.js:51
#: erpnext/accounts/doctype/payment_request/payment_request.js:55
msgid "Sending Email"
msgstr "Skickar e-post"
msgstr ""
#. Option for the 'Detected Amount Format' (Select) field in DocType 'Bank
#. Statement Import Log'
@@ -50201,7 +50201,7 @@ msgstr "Ange Leverantör"
#: erpnext/stock/doctype/material_request/material_request.js:455
msgid "Set Supplier for All Items"
msgstr "Ange Leverantör för Alla Artiklar"
msgstr ""
#. Label of the set_target_warehouse (Link) field in DocType 'Sales Invoice'
#. Label of the set_warehouse (Link) field in DocType 'Purchase Order'
@@ -52616,7 +52616,7 @@ msgstr "Lager ej tillgängligt för Artikel {0} i Lager {1}."
#: erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py:1240
msgid "Stock not available to reserve for the Item {0} in Warehouse {1}."
msgstr "Lager är inte tillgängligt för reservation för artikel {0} i lager {1}."
msgstr ""
#: erpnext/selling/page/point_of_sale/pos_controller.js:826
msgid "Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2} {3}."
@@ -55293,7 +55293,7 @@ msgstr "Konto under Skuld eller Eget Kapital, där Resultat Bokförs"
#: erpnext/accounts/doctype/account/account.py:222
msgid "The account type of {0} cannot be changed from {1} because stock ledger entries exist against it."
msgstr "Konto typ {0} kan inte ändras från {1} eftersom det finns lager poster mot den."
msgstr ""
#: erpnext/accounts/doctype/payment_request/payment_request.py:1016
msgid "The allocated amount is greater than the outstanding amount of Payment Request {0}"
@@ -55451,7 +55451,7 @@ msgstr "Följande rader är dubbletter:"
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:130
msgid "The following vouchers are not submitted: {0}"
msgstr "Följande verifikationer är inte godkända: {0}"
msgstr ""
#: erpnext/stock/doctype/material_request/material_request.py:1062
msgid "The following {0} were created: {1}"
@@ -55963,7 +55963,7 @@ msgstr "Detta dokument är över gräns med {0} {1} för post {4}. Skapa annan {
#: erpnext/templates/emails/appointment_confirmed.html:6
msgid "This email was sent from {0}"
msgstr "Detta e-postmeddelande skickades från {0}"
msgstr ""
#: erpnext/stock/doctype/delivery_note/delivery_note.js:496
msgid "This field is used to set the 'Customer'."
@@ -56109,7 +56109,7 @@ msgstr "Detta artikel filter har redan tillämpats för {0}"
#: erpnext/templates/emails/confirm_appointment.html:4
msgid "This link is valid for {0} minutes"
msgstr "Denna länk är giltig i {0} minuter"
msgstr ""
#: erpnext/www/banking.py:35
msgid "This method is only meant for developer mode"
@@ -56230,7 +56230,7 @@ msgstr "Detta värde ska användas när ingen matchande Gemensam Kod för post h
#: erpnext/www/book_appointment/verify/index.py:18
msgid "This verification link is invalid. Please book the appointment again."
msgstr "Denna verifiering länk är ogiltig. Boka ny tid."
msgstr ""
#: banking/src/components/features/Settings/Preferences.tsx:86
msgid "This will automatically run transaction matching rules on unreconciled transactions every hour."
@@ -58037,7 +58037,7 @@ msgstr "Transaktioner mot bolag finns redan! Kontoplan kan endast importeras fö
#. 'Customer'
#: erpnext/selling/doctype/customer/customer.json
msgid "Transactions are blocked when the outstanding balance exceeds the credit limit. When Restrict Customer Over Billing is enabled, new invoices are also blocked when the customer's overdue amount exceeds the Overdue Limit."
msgstr "Transaktioner blockeras när det utestående saldo överskrider kredit gräns. När funktion ”Begränsa Kund Överfakturering” är aktiverad blockeras även nya fakturor när kundens förfallna belopp överskrider gräns för förfallna fordringar."
msgstr ""
#: banking/src/components/features/BankStatementImporter/CSV/StatementDetails.tsx:239
msgid "Transactions to be imported into the system"
@@ -58658,7 +58658,7 @@ msgstr "Ångra Tilldelningar"
#: erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py:375
msgid "Unable to Repost Accounting Ledger"
msgstr "Kunde inte Återbokföra Bokföring Register"
msgstr ""
#: erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py:477
msgid "Unable to fetch DocType details. Please contact system administrator."
@@ -59601,7 +59601,7 @@ msgstr "Användare med denna roll tillåts att överleverera/ta emot ordrar öve
#. field in DocType 'Accounts Settings'
#: erpnext/accounts/doctype/accounts_settings/accounts_settings.json
msgid "Users with this role can still submit invoices for customers who have crossed their Overdue Limit."
msgstr "Användare med denna roll kan fortfarande godkänna fakturor för kunder som överskridit överfakturering gräns."
msgstr ""
#. Description of the 'Role to Notify on Depreciation Failure' (Link) field in
#. DocType 'Accounts Settings'
@@ -60148,12 +60148,12 @@ msgstr "Risk Kapital"
#. 'Appointment Booking Settings'
#: erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
msgid "Verification Link Expiry Duration"
msgstr "Verifiering Länk Utgångstid"
msgstr ""
#. Label of the verification_token (Data) field in DocType 'Appointment'
#: erpnext/crm/doctype/appointment/appointment.json
msgid "Verification Token"
msgstr "Verifiering Kod"
msgstr ""
#: erpnext/www/book_appointment/verify/index.html:15
msgid "Verification failed please check the link"
@@ -60161,7 +60161,7 @@ msgstr "Verifiering misslyckades, kontrollera länk"
#: erpnext/www/book_appointment/verify/index.py:38
msgid "Verification link has expired."
msgstr "Verifiering Länk har upphört."
msgstr ""
#. Label of the verified_by (Data) field in DocType 'Quality Inspection'
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json
@@ -61019,7 +61019,7 @@ msgstr "Vi kan se att {0} görs mot {1}. Om du vill att {1} s utestående ska up
#: erpnext/templates/emails/appointment_confirmed.html:3
msgid "We look forward to meeting you"
msgstr "Vi ser fram emot att träffa dig"
msgstr ""
#: banking/src/pages/BankStatementImporter.tsx:169
msgid "We support uploading CSV, XLSX, XLS and PDF files. Please make sure the file contains the correct columns."
@@ -62130,7 +62130,7 @@ msgstr "Ditt Namn"
#: erpnext/templates/emails/appointment_confirmed.html:2
msgid "Your email has been verified and your appointment has been confirmed for {0}"
msgstr "Din e-post adress har verifierats och bokad tid har bekräftats för {0}"
msgstr ""
#: erpnext/www/book_appointment/verify/index.html:11
msgid "Your email has been verified and your appointment has been scheduled"
@@ -62955,7 +62955,7 @@ msgstr "{0} vy stöds för närvarande inte i Anpassad Bokslut Rapport."
#: erpnext/stock/doctype/material_request/material_request.py:732
msgid "{0} was set to today for items whose requested date has passed"
msgstr "{0} angavs till idag för artiklar vars begärda datum har passerat"
msgstr ""
#: erpnext/accounts/doctype/payment_term/payment_term.js:19
msgid "{0} will be given as discount."

View File

@@ -881,15 +881,10 @@ def reset_item_valuation_rate(item_code, warehouse_list=None, qty=None, rate=Non
warehouse_list = [warehouse_list]
if not warehouse_list:
# Reconcile every warehouse the item has a non-zero balance in -- including
# negative balances left by other tests. get_valuation_rate averages
# Sum(stock_value)/Sum(actual_qty) across all bins, so a leftover negative
# balance in one warehouse can cancel the reset qty elsewhere and make the
# average collapse to 0, which is a source of flaky BOM-cost failures.
warehouse_list = frappe.db.sql_list(
"""
select warehouse from `tabBin`
where item_code=%s and actual_qty != 0
where item_code=%s and actual_qty > 0
""",
item_code,
)

View File

@@ -159,9 +159,6 @@ class DeprecatedBatchNoValuation:
if self.sle.name:
query = query.where(sle.name != self.sle.name)
if getattr(self, "stock_closing_from_datetime", None):
query = query.where(sle.posting_datetime >= self.stock_closing_from_datetime)
return query.run(as_dict=True)
@deprecated(

View File

@@ -5611,66 +5611,6 @@ class TestPurchaseReceipt(ERPNextTestSuite):
self.assertEqual(frappe.parse_json(stock_queue), [[20, 0.0]])
def test_purchase_return_valuation_for_batchwise_valuation_batch(self):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
item_code = make_item(
"Test Purchase Return Batchwise Valn Item",
{
"is_stock_item": 1,
"has_batch_no": 1,
"batch_number_series": "BN-TPRBWV-.#####",
},
).name
batch_no = "BN-TPRBWV-00001"
batch = frappe.new_doc("Batch").update({"batch_id": batch_no, "item": item_code}).insert()
self.assertEqual(batch.use_batchwise_valuation, 1)
warehouse = "_Test Warehouse - _TC"
pr = make_purchase_receipt(
item_code=item_code,
qty=100,
rate=1000,
warehouse=warehouse,
batch_no=batch_no,
use_serial_batch_fields=1,
)
make_purchase_receipt(
item_code=item_code,
qty=100,
rate=400,
warehouse=warehouse,
batch_no=batch_no,
use_serial_batch_fields=1,
)
create_delivery_note(
item_code=item_code,
qty=100,
warehouse=warehouse,
batch_no=batch_no,
use_serial_batch_fields=1,
)
return_pr = make_return_doc("Purchase Receipt", pr.name)
return_pr.submit()
sle = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_no": return_pr.name, "is_cancelled": 0},
["stock_value_difference", "qty_after_transaction", "stock_value", "serial_and_batch_bundle"],
as_dict=True,
)
self.assertEqual(flt(sle.qty_after_transaction), 0.0)
self.assertEqual(flt(sle.stock_value_difference, 2), -70000.0)
self.assertEqual(flt(sle.stock_value, 2), 0.0)
rate = frappe.db.get_value(
"Serial and Batch Entry", {"parent": sle.serial_and_batch_bundle}, "incoming_rate"
)
self.assertEqual(flt(rate, 2), 700.0)
def test_negative_stock_error_for_purchase_return(self):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry

View File

@@ -414,13 +414,6 @@ class SerialandBatchBundle(Document):
valuation_method = get_valuation_method(self.item_code, self.company)
# An outward return must go out at the batch's current average rate for a
# batchwise valuation batch. The original receipt rate is only correct while
# the batch still holds stock at that rate; once other receipts have changed
# the average, removing at the original rate strands a residue in the batch
# value (negative when returning the costlier receipt).
batchwise_avg_rates = self.get_batchwise_return_avg_rates()
stock_queue = []
non_batchwise_batches = []
if not self.has_serial_no and valuation_method == "FIFO":
@@ -454,12 +447,6 @@ class SerialandBatchBundle(Document):
batches = sorted(list(valuation_details["batches"].keys()))
valuation_rate = valuation_details["batches"].get(batches[cint(row.idx) - 1])
# a batch with an available balance goes out at its current average rate (a
# valid 0.0 included); the original receipt rate applies only when there is
# no balance to average
if not row.serial_no and row.batch_no in batchwise_avg_rates:
valuation_rate = batchwise_avg_rates[row.batch_no]
row.incoming_rate = flt(valuation_rate)
row.stock_value_difference = flt(row.qty) * flt(row.incoming_rate)
@@ -488,43 +475,6 @@ class SerialandBatchBundle(Document):
elif self.type_of_transaction == "Inward":
self.set_incoming_rate_for_inward_transaction(row, save, prev_sle=prev_sle)
def get_batchwise_return_avg_rates(self):
from erpnext.stock.utils import get_valuation_method
if self.type_of_transaction != "Outward" or self.has_serial_no:
return {}
batch_nos = [d.batch_no for d in self.entries if d.batch_no]
if not batch_nos:
return {}
if get_valuation_method(
self.item_code, self.company
) == "Moving Average" and frappe.db.get_single_value(
"Stock Settings", "do_not_use_batchwise_valuation"
):
return {}
batchwise_batches = frappe.get_all(
"Batch",
filters={"name": ("in", batch_nos), "use_batchwise_valuation": 1},
pluck="name",
)
if not batchwise_batches:
return {}
# scoped to batchwise batches only, so BatchNoValuation's non-batchwise
# machinery never runs for them
sle = self.get_sle_for_outward_transaction()
sle.batch_nos = {batch_no: sle.batch_nos[batch_no] for batch_no in batchwise_batches}
sle.batchwise_valuation_batches = batchwise_batches
sn_obj = BatchNoValuation(sle=sle, item_code=self.item_code, warehouse=self.warehouse)
return {
batch_no: abs(flt(sn_obj.batch_avg_rate.get(batch_no)))
for batch_no in batchwise_batches
if flt(sn_obj.available_qty.get(batch_no))
}
def validate_returned_serial_batch_no(self, return_against, row, original_inv_details):
if frappe.flags.through_repost_item_valuation and not frappe.in_test:
return

View File

@@ -4,7 +4,7 @@
import json
import frappe
from frappe.utils import add_days, add_to_date, flt, nowtime, today
from frappe.utils import flt, nowtime, today
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
@@ -1601,190 +1601,3 @@ class TestSerialandBatchBundleLogic(ERPNextTestSuite):
self.assertNotIn(bundles[1], bundle_wise_serial_nos)
self.assertEqual(bundle_wise_serial_nos[bundles[0]], [serial_no])
@ERPNextTestSuite.change_settings(
"Stock Settings", {"auto_create_serial_and_batch_bundle_for_outward": 1}
)
def test_batchwise_valuation_for_same_posting_datetime_entries(self):
# an inward at a different rate and multiple outward rows with the same
# item and warehouse share the same posting datetime, the tie-breaking
# must include the same-timestamp entries which are already part of the
# ledger and must not let the outward rows count each other
item_code = make_item(
"Test Batchwise Same Posting Datetime Item 1",
properties={
"is_stock_item": 1,
"has_batch_no": 1,
"create_new_batch": 1,
"batch_number_series": "TBSPD-ITEM1-.#####",
"valuation_method": "FIFO",
},
).name
warehouse = "_Test Warehouse - _TC"
receipt = make_stock_entry(
item_code=item_code,
qty=10,
rate=100,
target=warehouse,
posting_date=add_days(today(), -5),
posting_time="12:00:00",
)
batch_no = get_batch_from_bundle(receipt.items[0].serial_and_batch_bundle)
self.assertTrue(frappe.db.get_value("Batch", batch_no, "use_batchwise_valuation"))
# same posting datetime as the outward rows below, at a different rate
make_stock_entry(
item_code=item_code,
qty=20,
rate=250,
target=warehouse,
batch_no=batch_no,
use_serial_batch_fields=1,
posting_date=add_days(today(), -3),
posting_time="12:00:00",
)
issue = make_stock_entry(
item_code=item_code,
qty=2,
source=warehouse,
posting_date=add_days(today(), -3),
posting_time="12:00:00",
do_not_save=True,
)
for qty in [3, 4]:
issue.append(
"items",
{
"item_code": item_code,
"s_warehouse": warehouse,
"qty": qty,
"conversion_factor": 1,
},
)
issue.save()
issue.submit()
# (10 * 100 + 20 * 250) / 30 = 200
self.assert_batchwise_outgoing_rate(item_code, outgoing_rate=200.0, balance_value=4200.0)
# backdated receipt reposts the same posting datetime cluster
make_stock_entry(
item_code=item_code,
qty=10,
rate=100,
target=warehouse,
batch_no=batch_no,
use_serial_batch_fields=1,
posting_date=add_days(today(), -4),
posting_time="12:00:00",
)
# (20 * 100 + 20 * 250) / 40 = 175
self.assert_batchwise_outgoing_rate(item_code, outgoing_rate=175.0, balance_value=5425.0)
@ERPNextTestSuite.change_settings(
"Stock Settings", {"auto_create_serial_and_batch_bundle_for_outward": 1}
)
def test_batchwise_valuation_when_bundle_created_before_the_sle(self):
# a bundle can be created (drafted) much before / after its SLE, the
# tie-breaking for the same posting datetime entries must follow the
# SLE creation and not the bundle creation
item_code = make_item(
"Test Batchwise Same Posting Datetime Item 2",
properties={
"is_stock_item": 1,
"has_batch_no": 1,
"create_new_batch": 1,
"batch_number_series": "TBSPD-ITEM2-.#####",
"valuation_method": "FIFO",
},
).name
warehouse = "_Test Warehouse - _TC"
receipt = make_stock_entry(
item_code=item_code,
qty=10,
rate=100,
target=warehouse,
posting_date=add_days(today(), -5),
posting_time="12:00:00",
)
batch_no = get_batch_from_bundle(receipt.items[0].serial_and_batch_bundle)
# inward at a different rate, same posting datetime as the outward below
inward = make_stock_entry(
item_code=item_code,
qty=10,
rate=200,
target=warehouse,
batch_no=batch_no,
use_serial_batch_fields=1,
posting_date=add_days(today(), -3),
posting_time="12:00:00",
)
outward = make_stock_entry(
item_code=item_code,
qty=10,
source=warehouse,
posting_date=add_days(today(), -3),
posting_time="12:00:00",
)
# simulate the inward's bundle drafted after the outward's SLE, the
# bundle creation timeline no longer matches the SLE creation timeline
outward_sle_creation = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_no": outward.name, "is_cancelled": 0},
"creation",
)
frappe.db.set_value(
"Serial and Batch Bundle",
inward.items[0].serial_and_batch_bundle,
"creation",
add_to_date(outward_sle_creation, minutes=30),
update_modified=False,
)
repost = frappe.get_doc(
{
"doctype": "Repost Item Valuation",
"based_on": "Item and Warehouse",
"item_code": item_code,
"warehouse": warehouse,
"posting_date": add_days(today(), -6),
"posting_time": "00:00:00",
"allow_negative_stock": 1,
}
)
repost.submit()
# (10 * 100 + 10 * 200) / 20 = 150, the inward precedes the outward as
# per the SLE creation even though its bundle was created afterwards
self.assert_batchwise_outgoing_rate(item_code, outgoing_rate=150.0, balance_value=1500.0)
def assert_batchwise_outgoing_rate(self, item_code, outgoing_rate, balance_value):
sl_entries = frappe.get_all(
"Stock Ledger Entry",
filters={"item_code": item_code, "is_cancelled": 0},
fields=["actual_qty", "stock_value_difference", "stock_value"],
order_by="posting_datetime, creation",
)
for sle in sl_entries:
if sle.actual_qty > 0:
continue
self.assertEqual(flt(sle.stock_value_difference, 2), flt(sle.actual_qty * outgoing_rate, 2))
self.assertEqual(flt(sl_entries[-1].stock_value, 2), flt(balance_value, 2))

View File

@@ -10,51 +10,9 @@ from frappe.desk.form.load import get_attachments
from frappe.model.document import Document
from frappe.utils import add_days, get_date_str, get_link_to_form, nowtime, parse_json
from frappe.utils.background_jobs import enqueue
from frappe.utils.caching import request_cache
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
SCOPE_FIELDS = ("warehouse", "item_code", "item_group", "warehouse_type")
def apply_unscoped_filters(filters):
meta = frappe.get_meta("Stock Closing Entry")
for fieldname in SCOPE_FIELDS:
if meta.has_field(fieldname):
filters[fieldname] = ("is", "not set")
return filters
def get_closing_entry_for_closed_period(company):
closed_upto = frappe.db.get_value(
"Period Closing Voucher", {"docstatus": 1, "company": company}, [{"MAX": "period_end_date"}]
)
if not closed_upto:
return None
return _get_completed_closing_entry(company, str(closed_upto))
@request_cache
def _get_completed_closing_entry(company, closed_upto):
filters = apply_unscoped_filters(
{
"company": company,
"docstatus": 1,
"status": "Completed",
"to_date": ("<=", closed_upto),
}
)
return frappe.db.get_value(
"Stock Closing Entry",
filters,
["name", "to_date"],
order_by="to_date desc",
as_dict=True,
)
class StockClosingEntry(Document):
# begin: auto-generated types
@@ -110,7 +68,7 @@ class StockClosingEntry(Document):
)
)
for fieldname in SCOPE_FIELDS:
for fieldname in ["warehouse", "item_code", "item_group", "warehouse_type"]:
if self.get(fieldname):
query = query.where(table[fieldname] == self.get(fieldname))
@@ -128,30 +86,14 @@ class StockClosingEntry(Document):
self.enqueue_job()
def on_cancel(self):
self.validate_closed_period_lock()
self.set_status(save=True)
self.remove_stock_closing()
def validate_closed_period_lock(self):
pcv = frappe.db.get_value(
"Period Closing Voucher",
{"company": self.company, "docstatus": 1, "period_end_date": (">=", self.to_date)},
"name",
)
if pcv:
frappe.throw(
_(
"Stock Closing Entry {0} belongs to a closed accounting period. Cancel the Period Closing Voucher {1} first."
).format(self.name, get_link_to_form("Period Closing Voucher", pcv)),
title=_("Closed Period"),
)
def remove_stock_closing(self):
table = frappe.qb.DocType("Stock Closing Balance")
frappe.qb.from_(table).delete().where(table.stock_closing_entry == self.name).run()
@frappe.whitelist(methods=["POST"])
@frappe.whitelist()
def enqueue_job(self):
self.db_set("status", "In Progress")
enqueue(prepare_closing_stock_balance, name=self.name, queue="long", timeout=1500)
@@ -161,9 +103,8 @@ class StockClosingEntry(Document):
).format(self.name)
)
@frappe.whitelist(methods=["POST"])
@frappe.whitelist()
def regenerate_closing_balance(self):
self.validate_closed_period_lock()
self.remove_stock_closing()
self.enqueue_job()

View File

@@ -7,10 +7,8 @@ from collections import defaultdict
import frappe
from frappe import _
from frappe.query_builder.functions import IfNull, Sum
from frappe.query_builder.functions import Sum
from frappe.utils import cint, flt, get_datetime
from pypika import Order
from pypika.analytics import RowNumber
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -55,15 +53,14 @@ def execute(filters=None):
data = []
conversion_factors = []
opening_rows = opening_row if isinstance(opening_row, list) else ([opening_row] if opening_row else [])
for row in opening_rows:
data.append(row)
if opening_row:
data.append(opening_row)
conversion_factors.append(0)
actual_qty = stock_value = 0
if opening_rows:
actual_qty = opening_rows[0].get("qty_after_transaction", 0)
stock_value = opening_rows[0].get("stock_value", 0)
if opening_row:
actual_qty = opening_row.get("qty_after_transaction")
stock_value = opening_row.get("stock_value")
available_serial_nos = {}
@@ -696,120 +693,43 @@ def get_opening_balance(filters, columns, sl_entries, inv_dimension_wise_value=N
if not (filters.item_code and filters.warehouse and filters.from_date):
return
item_codes = filters.item_code
if isinstance(item_codes, str):
item_codes = [item_codes]
from erpnext.stock.stock_ledger import get_previous_sle
warehouses = get_matching_warehouses(filters.warehouse)
if not warehouses:
return
project = None
if filters.get("project") and not frappe.get_all(
"Inventory Dimension", filters={"reference_document": "Project"}
):
project = filters.get("project")
sle_doctype = frappe.qb.DocType("Stock Ledger Entry")
sr_doctype = frappe.qb.DocType("Stock Reconciliation")
opening_reco_query = (
frappe.qb.from_(sle_doctype)
.inner_join(sr_doctype)
.on(sle_doctype.voucher_no == sr_doctype.name)
.select(sle_doctype.voucher_no)
.where(sle_doctype.docstatus < 2)
.where(sle_doctype.is_cancelled == 0)
.where(sle_doctype.item_code.isin(item_codes))
.where(sle_doctype.warehouse.isin(warehouses))
.where(sle_doctype.voucher_type == "Stock Reconciliation")
.where(sle_doctype.posting_date == filters.from_date)
.where(sr_doctype.purpose == "Opening Stock")
last_entry = get_previous_sle(
{
"item_code": filters.item_code,
"warehouse_condition": get_warehouse_condition(filters.warehouse),
"posting_date": filters.from_date,
"posting_time": "00:00:00",
"project": project,
},
for_report=True,
)
opening_reco_vouchers = set(opening_reco_query.run(pluck=True))
# check if any SLEs are actually Opening Stock Reconciliation
for sle in list(sl_entries):
if (
sle.get("voucher_type") == "Stock Reconciliation"
and sle.posting_date == filters.from_date
and frappe.db.get_value("Stock Reconciliation", sle.voucher_no, "purpose") == "Opening Stock"
):
last_entry = sle
sl_entries.remove(sle)
if opening_reco_vouchers:
sl_entries[:] = [sle for sle in sl_entries if sle.get("voucher_no") not in opening_reco_vouchers]
sle_cond = (sle_doctype.posting_date < filters.from_date) | (
(sle_doctype.posting_date == filters.from_date) & (sle_doctype.posting_time == "00:00:00")
)
if opening_reco_vouchers:
sle_cond = sle_cond | (
(sle_doctype.posting_date == filters.from_date)
& (sle_doctype.voucher_no.isin(list(opening_reco_vouchers)))
)
subq = (
frappe.qb.from_(sle_doctype)
.select(
sle_doctype.qty_after_transaction,
sle_doctype.stock_value,
RowNumber()
.over(sle_doctype.item_code, sle_doctype.warehouse)
.orderby(sle_doctype.posting_datetime, sle_doctype.creation, sle_doctype.name, order=Order.desc)
.as_("rn"),
)
.where(sle_doctype.docstatus < 2)
.where(sle_doctype.is_cancelled == 0)
.where(sle_doctype.item_code.isin(item_codes))
.where(sle_doctype.warehouse.isin(warehouses))
.where(sle_cond)
)
for field in ["voucher_no", "project", "company"]:
if filters.get(field):
subq = subq.where(sle_doctype[field] == filters.get(field))
inventory_dimension_fields = get_inventory_dimension_fields()
if inventory_dimension_fields:
for fieldname in inventory_dimension_fields:
if filters.get(fieldname):
subq = subq.where(sle_doctype[fieldname].isin(filters.get(fieldname)))
query = (
frappe.qb.from_(subq)
.select(
IfNull(Sum(subq.qty_after_transaction), 0.0).as_("total_qty"),
IfNull(Sum(subq.stock_value), 0.0).as_("total_stock_value"),
)
.where(subq.rn == 1)
)
res = query.run(as_dict=True)
total_qty = flt(res[0].total_qty) if res else 0.0
total_stock_value = flt(res[0].total_stock_value) if res else 0.0
valuation_rate = flt(total_stock_value / total_qty) if total_qty else 0.0
return {
row = {
"item_code": _("'Opening'"),
"qty_after_transaction": total_qty,
"valuation_rate": valuation_rate,
"stock_value": total_stock_value,
"qty_after_transaction": last_entry.get("qty_after_transaction", 0),
"valuation_rate": last_entry.get("valuation_rate", 0),
"stock_value": last_entry.get("stock_value", 0),
}
def get_matching_warehouses(warehouses):
if not warehouses:
return []
if isinstance(warehouses, str):
warehouses = [warehouses]
warehouse_details = frappe.get_all(
"Warehouse",
filters={"name": ("in", warehouses)},
fields=["lft", "rgt"],
)
if not warehouse_details:
return warehouses
wh = frappe.qb.DocType("Warehouse")
cond = None
for d in warehouse_details:
c = (wh.lft >= d.lft) & (wh.rgt <= d.rgt)
cond = c if cond is None else (cond | c)
matching = (frappe.qb.from_(wh).select(wh.name).where(cond)).run(pluck=True)
return matching if matching else warehouses
return row
def get_warehouse_condition(warehouses):
@@ -865,15 +785,7 @@ def get_opening_balance_for_inv_dimension(filters, inv_dimension_wise_value):
if not filters.item_code or not filters.warehouse or not filters.from_date:
return
item_codes = filters.get("item_code")
if isinstance(item_codes, str):
item_codes = [item_codes]
warehouses = filters.get("warehouse")
if isinstance(warehouses, str):
warehouses = [warehouses]
if len(item_codes) > 1 or len(warehouses) > 1:
if len(filters.get("item_code")) > 1 or len(filters.get("warehouse")) > 1:
return
sl_doctype = frappe.qb.DocType("Stock Ledger Entry")
@@ -893,11 +805,17 @@ def get_opening_balance_for_inv_dimension(filters, inv_dimension_wise_value):
)
)
if item_codes:
query = query.where(sl_doctype.item_code.isin(item_codes))
if filters.get("item_code"):
if isinstance(filters.item_code, list | tuple):
query = query.where(sl_doctype.item_code.isin(filters.item_code))
else:
query = query.where(sl_doctype.item_code == filters.item_code)
if warehouses:
query = query.where(sl_doctype.warehouse.isin(warehouses))
if filters.get("warehouse"):
if isinstance(filters.warehouse, list | tuple):
query = query.where(sl_doctype.warehouse.isin(filters.warehouse))
else:
query = query.where(sl_doctype.warehouse == filters.warehouse)
for key, value in inv_dimension_wise_value.items():
if isinstance(value, list | tuple):

View File

@@ -4,333 +4,18 @@
import frappe
from frappe.utils import add_days, today
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.report.stock_ledger.stock_ledger import execute
from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
make_serial_item_with_serial,
)
from erpnext.tests.utils import ERPNextTestSuite
WAREHOUSE = "Stores - _TC"
class TestStockLedgerReport(ERPNextTestSuite):
"""Correctness tests for the Stock Ledger report.
A shared `make_movements`/`run` pair keeps each test small without persisting
any data: movements are created per test and rolled back, while the report runs
read-only. Tests reuse bootstrap items and transact in `Stores - _TC`, which
starts clean (zero balance) for these items.
"""
def make_movements(self, item_code, movements):
for movement in movements:
make_stock_entry(item_code=item_code, **movement)
def run_report(self, item_code, from_date=None, to_date=None):
filters = frappe._dict(
class TestStockLedgerReeport(ERPNextTestSuite):
def setUp(self) -> None:
make_serial_item_with_serial(self, "_Test Stock Report Serial Item")
self.filters = frappe._dict(
company="_Test Company",
from_date=from_date or add_days(today(), -1),
to_date=to_date or today(),
item_code=[item_code],
warehouse=WAREHOUSE,
)
return list(execute(filters)[1])
def test_in_out_quantities_and_running_balance(self):
item = "_Test Item"
self.make_movements(
item,
[
{"qty": 10, "to_warehouse": WAREHOUSE, "basic_rate": 100},
{"qty": 4, "from_warehouse": WAREHOUSE},
],
)
rows = self.run_report(item)
receipt = next(row for row in rows if row.get("in_qty"))
issue = next(row for row in rows if row.get("out_qty"))
self.assertEqual(receipt["in_qty"], 10)
self.assertEqual(receipt["qty_after_transaction"], 10)
self.assertEqual(issue["out_qty"], -4)
self.assertEqual(issue["qty_after_transaction"], 6)
def test_opening_balance_reflects_movements_before_from_date(self):
item = "_Test Item"
self.make_movements(
item,
[
{
"qty": 10,
"to_warehouse": WAREHOUSE,
"basic_rate": 100,
"posting_date": add_days(today(), -10),
},
{"qty": 4, "from_warehouse": WAREHOUSE, "posting_date": today()},
],
)
rows = self.run_report(item, from_date=add_days(today(), -5), to_date=today())
# the receipt predates the range, so it surfaces as the opening balance
self.assertEqual(rows[0]["item_code"], "'Opening'")
self.assertEqual(rows[0]["qty_after_transaction"], 10)
# the in-range issue draws down from the opening balance
issue = next(row for row in rows if row.get("out_qty"))
self.assertEqual(issue["qty_after_transaction"], 6)
def test_filters_to_requested_item_only(self):
item_a = "_Test Item"
item_b = "_Test Item 2"
self.make_movements(item_a, [{"qty": 5, "to_warehouse": WAREHOUSE, "basic_rate": 100}])
self.make_movements(item_b, [{"qty": 7, "to_warehouse": WAREHOUSE, "basic_rate": 100}])
rows = self.run_report(item_a)
item_codes = {row["item_code"] for row in rows if row.get("voucher_no")}
self.assertEqual(item_codes, {item_a})
def test_multi_item_opening_balance_with_and_without_transactions(self):
item_a = "_Test Item"
item_b = "_Test Item 2"
self.make_movements(
item_a,
[
{
"qty": 10,
"to_warehouse": WAREHOUSE,
"basic_rate": 100,
"posting_date": add_days(today(), -10),
}
],
)
self.make_movements(
item_b,
[{"qty": 5, "to_warehouse": WAREHOUSE, "basic_rate": 50, "posting_date": add_days(today(), -10)}],
)
self.make_movements(
item_a,
[{"qty": 2, "from_warehouse": WAREHOUSE, "posting_date": today()}],
)
filters = frappe._dict(
company="_Test Company",
from_date=add_days(today(), -5),
to_date=today(),
item_code=[item_a, item_b],
warehouse=WAREHOUSE,
)
columns, rows = execute(filters)
opening_rows = [row for row in rows if row.get("item_code") == "'Opening'"]
self.assertEqual(len(opening_rows), 1)
self.assertEqual(opening_rows[0]["qty_after_transaction"], 15)
def test_multi_warehouse_opening_balance_aggregation(self):
item = "_Test Item"
warehouse_1 = "Stores - _TC"
warehouse_2 = "Finished Goods - _TC"
self.make_movements(
item,
[
{
"qty": 10,
"to_warehouse": warehouse_1,
"basic_rate": 100,
"posting_date": add_days(today(), -10),
},
{
"qty": 20,
"to_warehouse": warehouse_2,
"basic_rate": 100,
"posting_date": add_days(today(), -10),
},
],
)
filters = frappe._dict(
company="_Test Company",
from_date=add_days(today(), -5),
to_date=today(),
item_code=[item],
warehouse=[warehouse_1, warehouse_2],
)
columns, rows = execute(filters)
opening_rows = [row for row in rows if row.get("item_code") == "'Opening'"]
self.assertEqual(len(opening_rows), 1)
self.assertEqual(opening_rows[0]["qty_after_transaction"], 30)
def test_opening_stock_reconciliation_on_from_date_non_midnight_time(self):
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_stock_reconciliation,
)
item = "_Test Item"
from_date = today()
sr = create_stock_reconciliation(
item_code=item,
warehouse=WAREHOUSE,
qty=25,
rate=100,
posting_date=from_date,
posting_time="10:30:00",
purpose="Opening Stock",
do_not_submit=False,
)
filters = frappe._dict(
company="_Test Company",
from_date=from_date,
to_date=from_date,
item_code=[item],
warehouse=WAREHOUSE,
)
columns, rows = execute(filters)
opening_rows = [row for row in rows if row.get("item_code") == "'Opening'"]
self.assertEqual(len(opening_rows), 1)
self.assertEqual(opening_rows[0]["qty_after_transaction"], 25)
# Ensure the Opening Stock Reconciliation is not duplicated in detail transaction rows
reco_rows = [row for row in rows if row.get("voucher_no") == sr.name]
self.assertEqual(len(reco_rows), 0)
def test_backdated_sle_independent_maxima_handling(self):
item = "_Test Item"
# Entry 1: Later posting date (2026-07-20), created first
self.make_movements(
item,
[
{
"qty": 10,
"to_warehouse": WAREHOUSE,
"basic_rate": 100,
"posting_date": add_days(today(), -10),
}
],
)
# Entry 2: Backdated posting date (2026-07-15), created LATER
self.make_movements(
item,
[
{
"qty": 5,
"to_warehouse": WAREHOUSE,
"basic_rate": 100,
"posting_date": add_days(today(), -15),
}
],
)
filters = frappe._dict(
company="_Test Company",
from_date=add_days(today(), -5),
to_date=today(),
item_code=[item],
warehouse=WAREHOUSE,
)
columns, rows = execute(filters)
opening_rows = [row for row in rows if row.get("item_code") == "'Opening'"]
self.assertEqual(len(opening_rows), 1)
# Should correctly pick the latest posting date entry (15 Qty) despite backdated creation order
self.assertEqual(opening_rows[0]["qty_after_transaction"], 15)
def test_filtered_opening_balance_does_not_pick_excluded_creation_entry(self):
item = "_Test Item"
posting_date = add_days(today(), -10)
posting_time = "09:00:00"
included_entry = make_stock_entry(
item_code=item,
qty=10,
to_warehouse=WAREHOUSE,
basic_rate=100,
posting_date=posting_date,
posting_time=posting_time,
)
make_stock_entry(
item_code=item,
qty=50,
to_warehouse=WAREHOUSE,
basic_rate=100,
posting_date=posting_date,
posting_time=posting_time,
)
filters = frappe._dict(
company="_Test Company",
from_date=add_days(today(), -5),
to_date=today(),
item_code=[item],
warehouse=WAREHOUSE,
voucher_no=included_entry.name,
)
columns, rows = execute(filters)
opening_rows = [row for row in rows if row.get("item_code") == "'Opening'"]
self.assertEqual(len(opening_rows), 1)
self.assertEqual(opening_rows[0]["qty_after_transaction"], 10)
def test_tied_creation_terminal_sle_is_not_summed_twice(self):
item = "_Test Item"
posting_date = add_days(today(), -10)
posting_time = "09:00:00"
stock_entry_1 = make_stock_entry(
item_code=item,
qty=10,
to_warehouse=WAREHOUSE,
basic_rate=100,
posting_date=posting_date,
posting_time=posting_time,
)
stock_entry_2 = make_stock_entry(
item_code=item,
qty=5,
to_warehouse=WAREHOUSE,
basic_rate=100,
posting_date=posting_date,
posting_time=posting_time,
)
sle_rows = frappe.get_all(
"Stock Ledger Entry",
filters={
"voucher_type": "Stock Entry",
"voucher_no": ("in", [stock_entry_1.name, stock_entry_2.name]),
"item_code": item,
"warehouse": WAREHOUSE,
"is_cancelled": 0,
},
fields=["name", "qty_after_transaction"],
order_by="name desc",
)
self.assertEqual(len(sle_rows), 2)
for sle in sle_rows:
frappe.db.set_value(
"Stock Ledger Entry",
sle.name,
"creation",
"2026-01-01 00:00:00.000000",
update_modified=False,
)
filters = frappe._dict(
company="_Test Company",
from_date=add_days(today(), -5),
to_date=today(),
item_code=[item],
warehouse=WAREHOUSE,
)
columns, rows = execute(filters)
opening_rows = [row for row in rows if row.get("item_code") == "'Opening'"]
self.assertEqual(len(opening_rows), 1)
self.assertEqual(opening_rows[0]["qty_after_transaction"], sle_rows[0].qty_after_transaction)
self.assertNotEqual(
opening_rows[0]["qty_after_transaction"],
sum(sle.qty_after_transaction for sle in sle_rows),
from_date=today(),
to_date=add_days(today(), 30),
item_code=["_Test Stock Report Serial Item"],
)

View File

@@ -820,14 +820,13 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
"Serial and Batch Bundle", self.sle.serial_and_batch_bundle, "total_amount"
)
else:
entries = self.get_batch_stock_before_date()
self.stock_value_change = 0.0
self.batch_avg_rate = defaultdict(float)
self.available_qty = defaultdict(float)
self.stock_value_differece = defaultdict(float)
self.seed_from_stock_closing_balance()
for ledger in self.get_batch_stock_before_date():
for ledger in entries:
self.stock_value_differece[ledger.batch_no] += flt(ledger.incoming_rate)
self.available_qty[ledger.batch_no] += flt(ledger.qty)
@@ -835,52 +834,6 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
self.calculate_avg_rate_for_non_batchwise_valuation()
self.set_stock_value_difference()
def seed_from_stock_closing_balance(self):
self.stock_closing_from_datetime = None
closing_entry = self.get_closing_entry_for_seeding()
if not closing_entry:
return
from erpnext.stock.utils import get_combine_datetime
self.stock_closing_from_datetime = get_combine_datetime(
add_days(closing_entry.to_date, 1), "00:00:00"
)
for row in self.get_stock_closing_balance_entries(closing_entry.name):
self.stock_value_differece[row.batch_no] += flt(row.stock_value_difference)
self.available_qty[row.batch_no] += flt(row.actual_qty)
def get_closing_entry_for_seeding(self):
from erpnext.stock.doctype.stock_closing_entry.stock_closing_entry import (
get_closing_entry_for_closed_period,
)
if not self.batchwise_valuation_batches or not self.sle.posting_date:
return None
company = self.sle.company or frappe.get_cached_value("Warehouse", self.sle.warehouse, "company")
closing_entry = get_closing_entry_for_closed_period(company)
if not closing_entry or getdate(self.sle.posting_date) <= getdate(closing_entry.to_date):
return None
return closing_entry
def get_stock_closing_balance_entries(self, closing_entry):
table = frappe.qb.DocType("Stock Closing Balance")
return (
frappe.qb.from_(table)
.select(table.batch_no, table.actual_qty, table.stock_value_difference)
.where(
(table.stock_closing_entry == closing_entry)
& (table.item_code == self.sle.item_code)
& (table.warehouse == self.sle.warehouse)
& table.batch_no.isin(self.batchwise_valuation_batches)
& (table.inventory_dimension_key.isnull() | (table.inventory_dimension_key == ""))
)
).run(as_dict=True)
def get_batch_stock_before_date(self) -> list[dict]:
# Get batch wise stock value difference from Serial and Batch Bundle considering time condition
if not self.batchwise_valuation_batches:
@@ -888,45 +841,14 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
child = frappe.qb.DocType("Serial and Batch Entry")
sle_creation = self.sle.creation if self.sle.get("name") else None
if not self.sle.get("name") and self.sle.get("serial_and_batch_bundle"):
sle_creation = frappe.db.get_value(
"Stock Ledger Entry",
{"serial_and_batch_bundle": self.sle.serial_and_batch_bundle, "is_cancelled": 0},
"creation",
)
timestamp_condition = ""
if self.sle.posting_datetime:
timestamp_condition = child.posting_datetime < self.sle.posting_datetime
sle_table = frappe.qb.DocType("Stock Ledger Entry")
if sle_creation:
# bundle creation and SLE creation are different timelines (a
# bundle can be created much before its SLE), so break the tie
# using the creation of the bundle's own SLE
tie_condition = ExistsCriterion(
frappe.qb.from_(sle_table)
.select(sle_table.name)
.where(
(sle_table.serial_and_batch_bundle == child.parent)
& (sle_table.is_cancelled == 0)
& (sle_table.creation < sle_creation)
)
if self.sle.creation:
timestamp_condition |= (child.posting_datetime == self.sle.posting_datetime) & (
child.creation < self.sle.creation
)
else:
# the current entry is not yet in the ledger and will get the
# latest creation, so the same-timestamp entries which are
# already in the ledger precede it
tie_condition = ExistsCriterion(
frappe.qb.from_(sle_table)
.select(sle_table.name)
.where(
(sle_table.serial_and_batch_bundle == child.parent) & (sle_table.is_cancelled == 0)
)
)
timestamp_condition |= (child.posting_datetime == self.sle.posting_datetime) & tie_condition
query = (
frappe.qb.from_(child)
@@ -956,9 +878,6 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
if timestamp_condition:
query = query.where(timestamp_condition)
if self.stock_closing_from_datetime:
query = query.where(child.posting_datetime >= self.stock_closing_from_datetime)
return query.run(as_dict=True)
def prepare_batches(self):
@@ -971,11 +890,6 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
self.batchwise_valuation_batches = []
self.non_batchwise_valuation_batches = []
if batchwise_batches := self.sle.get("batchwise_valuation_batches"):
self.batchwise_valuation_batches = list(batchwise_batches)
self.non_batchwise_valuation_batches = list(set(self.batches) - set(batchwise_batches))
return
if get_valuation_method(
self.sle.item_code, self.sle.company
) == "Moving Average" and frappe.get_single_value("Stock Settings", "do_not_use_batchwise_valuation"):

View File

@@ -56,32 +56,6 @@ class SerialNoExistsInFutureTransaction(frappe.ValidationError):
pass
def validate_stock_frozen_by_closing_entry(sl_entries):
from erpnext.stock.doctype.stock_closing_entry.stock_closing_entry import (
get_closing_entry_for_closed_period,
)
company = sl_entries[0].get("company")
if not company:
company = frappe.get_cached_value("Warehouse", sl_entries[0].get("warehouse"), "company")
closing_entry = get_closing_entry_for_closed_period(company)
if not closing_entry:
return
for sle in sl_entries:
if sle.get("posting_date") and getdate(sle.get("posting_date")) <= getdate(closing_entry.to_date):
frappe.throw(
_(
"Stock transactions dated on or before {0} are frozen because the period is closed and the Stock Closing Entry {1} has been generated. To make changes, cancel the Period Closing Voucher first."
).format(
frappe.bold(format_date(closing_entry.to_date)),
get_link_to_form("Stock Closing Entry", closing_entry.name),
),
title=_("Stock Frozen"),
)
def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
"""Create SL entries from SL entry dicts
@@ -96,8 +70,6 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc
from erpnext.controllers.stock_controller import future_sle_exists
if sl_entries:
validate_stock_frozen_by_closing_entry(sl_entries)
cancelled = sl_entries[0].get("is_cancelled")
if cancelled:
validate_cancellation(sl_entries)