Merge branch 'develop' of https://github.com/frappe/erpnext into dev_fix_french_chart_of_account

This commit is contained in:
Florian HENRY
2024-05-15 18:37:09 +02:00
7 changed files with 183 additions and 2 deletions

View File

@@ -29,7 +29,11 @@ jobs:
steps:
- name: Update notes
run: |
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' )
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/generate-notes -f tag_name=$RELEASE_TAG \
| jq -r '.body' \
| sed -E '/^\* (chore|ci|test|docs|style)/d' \
| sed -E 's/by @mergify //'
)
RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/tags/$RELEASE_TAG | jq -r '.id')
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/$RELEASE_ID -f body="$NEW_NOTES"

View File

@@ -205,6 +205,7 @@ class StockController(AccountsController):
"company": self.company,
"is_rejected": 1 if row.get("rejected_warehouse") else 0,
"use_serial_batch_fields": row.use_serial_batch_fields,
"via_landed_cost_voucher": via_landed_cost_voucher,
"do_not_submit": True if not via_landed_cost_voucher else False,
}

View File

@@ -5,7 +5,7 @@ import copy
import json
import frappe
from frappe import _
from frappe import _, bold
from frappe.model.document import Document
from frappe.query_builder import Interval
from frappe.query_builder.functions import Count, CurDate, UnixTimestamp
@@ -469,6 +469,13 @@ class Item(Document):
def validate_warehouse_for_reorder(self):
"""Validate Reorder level table for duplicate and conditional mandatory"""
warehouse_material_request_type: list[tuple[str, str]] = []
_warehouse_before_save = frappe._dict()
if not self.is_new() and self._doc_before_save:
_warehouse_before_save = {
d.name: d.warehouse for d in self._doc_before_save.get("reorder_levels") or []
}
for d in self.get("reorder_levels"):
if not d.warehouse_group:
d.warehouse_group = d.warehouse
@@ -485,6 +492,19 @@ class Item(Document):
if d.warehouse_reorder_level and not d.warehouse_reorder_qty:
frappe.throw(_("Row #{0}: Please set reorder quantity").format(d.idx))
if d.warehouse_group and d.warehouse:
if _warehouse_before_save.get(d.name) == d.warehouse:
continue
child_warehouses = get_child_warehouses(d.warehouse_group)
if d.warehouse not in child_warehouses:
frappe.throw(
_(
"Row #{0}: The warehouse {1} is not a child warehouse of a group warehouse {2}"
).format(d.idx, bold(d.warehouse), bold(d.warehouse_group)),
title=_("Incorrect Check in (group) Warehouse for Reorder"),
)
def stock_ledger_created(self):
if not hasattr(self, "_stock_ledger_created"):
self._stock_ledger_created = len(
@@ -1360,3 +1380,10 @@ def get_asset_naming_series():
from erpnext.assets.doctype.asset.asset import get_asset_naming_series
return get_asset_naming_series()
@frappe.request_cache
def get_child_warehouses(warehouse):
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
return get_child_warehouses(warehouse)

View File

@@ -889,6 +889,27 @@ class TestItem(FrappeTestCase):
self.assertEqual(data[0].description, item.description)
self.assertTrue("description" in data[0])
def test_group_warehouse_for_reorder_item(self):
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
item_doc = make_item("_Test Group Warehouse For Reorder Item", {"is_stock_item": 1})
warehouse = create_warehouse("_Test Warehouse - _TC")
warehouse_doc = frappe.get_doc("Warehouse", warehouse)
warehouse_doc.db_set("parent_warehouse", "")
item_doc.append(
"reorder_levels",
{
"warehouse": warehouse,
"warehouse_reorder_level": 10,
"warehouse_reorder_qty": 100,
"material_request_type": "Purchase",
"warehouse_group": "_Test Warehouse Group - _TC",
},
)
self.assertRaises(frappe.ValidationError, item_doc.save)
def set_item_variant_settings(fields):
doc = frappe.get_doc("Item Variant Settings")

View File

@@ -946,6 +946,128 @@ class TestLandedCostVoucher(FrappeTestCase):
frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"),
)
def test_do_not_validate_against_landed_cost_voucher_for_serial_for_legacy_pr(self):
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import get_auto_batch_nos
frappe.flags.ignore_serial_batch_bundle_validation = True
frappe.flags.use_serial_and_batch_fields = True
sn_item = "Test Don't Validate Against LCV For Serial NO for Legacy PR"
sn_item_doc = make_item(
sn_item,
{
"has_serial_no": 1,
"serial_no_series": "SN-ALCVTDVLCVSNO-.####",
"is_stock_item": 1,
},
)
serial_nos = [
"SN-ALCVTDVLCVSNO-0001",
"SN-ALCVTDVLCVSNO-0002",
"SN-ALCVTDVLCVSNO-0003",
"SN-ALCVTDVLCVSNO-0004",
"SN-ALCVTDVLCVSNO-0005",
]
for sn in serial_nos:
if not frappe.db.exists("Serial No", sn):
sn_doc = frappe.get_doc(
{
"doctype": "Serial No",
"item_code": sn_item,
"serial_no": sn,
}
)
sn_doc.insert()
warehouse = "_Test Warehouse - _TC"
company = frappe.db.get_value("Warehouse", warehouse, "company")
pr = make_purchase_receipt(
company=company,
warehouse=warehouse,
item_code=sn_item,
qty=5,
rate=100,
uom=sn_item_doc.stock_uom,
stock_uom=sn_item_doc.stock_uom,
)
pr.reload()
for sn in serial_nos:
sn_doc = frappe.get_doc("Serial No", sn)
sn_doc.db_set(
{
"warehouse": warehouse,
"status": "Active",
}
)
for row in pr.items:
if row.item_code == sn_item:
row.db_set("serial_no", ", ".join(serial_nos))
stock_ledger_entries = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": pr.name})
for sle in stock_ledger_entries:
doc = frappe.get_doc("Stock Ledger Entry", sle.name)
if doc.item_code == sn_item:
doc.db_set("serial_no", ", ".join(serial_nos))
dn = create_delivery_note(
company=company,
warehouse=warehouse,
item_code=sn_item,
qty=5,
rate=100,
uom=sn_item_doc.stock_uom,
stock_uom=sn_item_doc.stock_uom,
)
stock_ledger_entries = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": dn.name})
for sle in stock_ledger_entries:
doc = frappe.get_doc("Stock Ledger Entry", sle.name)
if doc.item_code == sn_item:
doc.db_set("serial_no", ", ".join(serial_nos))
frappe.flags.ignore_serial_batch_bundle_validation = False
frappe.flags.use_serial_and_batch_fields = False
lcv = make_landed_cost_voucher(
company=pr.company,
receipt_document_type="Purchase Receipt",
receipt_document=pr.name,
charges=20,
distribute_charges_based_on="Qty",
do_not_save=True,
)
lcv.get_items_from_purchase_receipts()
lcv.save()
lcv.submit()
pr.reload()
for row in pr.items:
self.assertEqual(row.valuation_rate, 104)
self.assertTrue(row.serial_and_batch_bundle)
self.assertEqual(
row.valuation_rate,
frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"),
)
lcv.cancel()
pr.reload()
for row in pr.items:
self.assertEqual(row.valuation_rate, 100)
self.assertTrue(row.serial_and_batch_bundle)
self.assertEqual(
row.valuation_rate,
frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"),
)
def make_landed_cost_voucher(**args):
args = frappe._dict(args)

View File

@@ -428,6 +428,9 @@ class SerialandBatchBundle(Document):
self.throw_error_message(f"The {self.voucher_type} # {self.voucher_no} should be submit first.")
def check_future_entries_exists(self):
if self.flags and self.flags.via_landed_cost_voucher:
return
if not self.has_serial_no:
return

View File

@@ -840,6 +840,9 @@ class SerialBatchCreation:
self.set_auto_serial_batch_entries_for_inward()
self.add_serial_nos_for_batch_item()
if hasattr(self, "via_landed_cost_voucher") and self.via_landed_cost_voucher:
doc.flags.via_landed_cost_voucher = self.via_landed_cost_voucher
self.set_serial_batch_entries(doc)
if not doc.get("entries"):
return frappe._dict({})