mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-10 07:49:29 +00:00
fix(stock): format serial numbers in builder prints
This commit is contained in:
@@ -2,10 +2,10 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class PurchaseInvoiceItem(Document):
|
class PurchaseInvoiceItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
|
||||||
|
|
||||||
from erpnext.assets.doctype.asset.depreciation import get_disposal_account_and_cost_center
|
from erpnext.assets.doctype.asset.depreciation import get_disposal_account_and_cost_center
|
||||||
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class SalesInvoiceItem(Document):
|
class SalesInvoiceItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class AssetCapitalizationStockItem(Document):
|
class AssetCapitalizationStockItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class AssetRepairConsumedItem(Document):
|
class AssetRepairConsumedItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class PurchaseReceiptItemSupplied(Document):
|
class PurchaseReceiptItemSupplied(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -383,12 +383,11 @@ pre_submit_validation_doctypes = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
pdf_body_html = "erpnext.stock.serial_batch_display.pdf_body_html"
|
|
||||||
|
|
||||||
extend_doctype_class["Data Import"] = "erpnext.stock.serial_batch_import.SerialBatchDataImport"
|
extend_doctype_class["Data Import"] = "erpnext.stock.serial_batch_import.SerialBatchDataImport"
|
||||||
|
|
||||||
doc_events = {
|
doc_events = {
|
||||||
"*": {
|
"*": {
|
||||||
|
"before_print": "erpnext.stock.serial_batch_display.set_serial_number_labels",
|
||||||
"validate": [
|
"validate": [
|
||||||
"erpnext.support.doctype.service_level_agreement.service_level_agreement.apply",
|
"erpnext.support.doctype.service_level_agreement.service_level_agreement.apply",
|
||||||
"erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.check_for_running_deletion_job",
|
"erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.check_for_running_deletion_job",
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class MaintenanceScheduleDetail(Document):
|
class MaintenanceScheduleDetail(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class MaintenanceScheduleItem(Document):
|
class MaintenanceScheduleItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class InstallationNoteItem(Document):
|
class InstallationNoteItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class DeliveryNoteItem(Document):
|
class DeliveryNoteItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import json
|
|||||||
import frappe
|
import frappe
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
|
|
||||||
from erpnext.stock.get_item_details import get_item_details, get_price_list_rate
|
from erpnext.stock.get_item_details import get_item_details, get_price_list_rate
|
||||||
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class PackedItem(Document):
|
class PackedItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
|
|
||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class PickListItem(Document):
|
class PickListItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class PurchaseReceiptItem(Document):
|
class PurchaseReceiptItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, bold
|
from frappe import _, bold
|
||||||
from frappe.model.document import Document
|
|
||||||
from frappe.utils import (
|
from frappe.utils import (
|
||||||
flt,
|
flt,
|
||||||
get_link_to_form,
|
get_link_to_form,
|
||||||
@@ -13,10 +12,11 @@ from frappe.utils import (
|
|||||||
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
|
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
|
||||||
OpeningEntryAccountError,
|
OpeningEntryAccountError,
|
||||||
)
|
)
|
||||||
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
from erpnext.stock.stock_ledger import get_previous_sle
|
from erpnext.stock.stock_ledger import get_previous_sle
|
||||||
|
|
||||||
|
|
||||||
class StockEntryDetail(Document):
|
class StockEntryDetail(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from datetime import date
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.core.doctype.role.role import get_users
|
from frappe.core.doctype.role.role import get_users
|
||||||
from frappe.model.document import Document
|
|
||||||
from frappe.query_builder.functions import Concat_ws, Max, Sum
|
from frappe.query_builder.functions import Concat_ws, Max, Sum
|
||||||
from frappe.utils import add_days, cint, flt, formatdate, get_datetime, getdate
|
from frappe.utils import add_days, cint, flt, formatdate, get_datetime, getdate
|
||||||
|
|
||||||
@@ -17,6 +16,7 @@ from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
|
|||||||
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
|
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos as get_parsed_serial_nos
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos as get_parsed_serial_nos
|
||||||
from erpnext.stock.serial_batch_bundle import SerialBatchBundle, get_serial_nos
|
from erpnext.stock.serial_batch_bundle import SerialBatchBundle, get_serial_nos
|
||||||
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class StockFreezeError(frappe.ValidationError):
|
class StockFreezeError(frappe.ValidationError):
|
||||||
@@ -38,7 +38,7 @@ class SerialNoInventoryDimensionError(frappe.ValidationError):
|
|||||||
exclude_from_linked_with = True
|
exclude_from_linked_with = True
|
||||||
|
|
||||||
|
|
||||||
class StockLedgerEntry(Document):
|
class StockLedgerEntry(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class StockReconciliationItem(Document):
|
class StockReconciliationItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
"""Display physical numbers while retaining document IDs for stock references."""
|
"""Display physical numbers while retaining document IDs for stock references."""
|
||||||
|
|
||||||
from copy import deepcopy
|
from copy import copy
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.model.document import Document
|
||||||
from frappe.utils import escape_html
|
from frappe.utils import escape_html
|
||||||
|
|
||||||
from erpnext.stock.serial_batch_identity import SerialBatchIdentity
|
from erpnext.stock.serial_batch_identity import SerialBatchIdentity
|
||||||
@@ -60,28 +61,46 @@ def report_number_columns(columns, rows):
|
|||||||
return columns, rows
|
return columns, rows
|
||||||
|
|
||||||
|
|
||||||
def pdf_body_html(template, args, **kwargs):
|
class SerialBatchReference(Document):
|
||||||
from frappe.utils.pdf import pdf_body_html as render_body
|
def get_formatted(
|
||||||
|
self, fieldname, doc=None, currency=None, absolute_value=False, translated=False, format=None
|
||||||
|
):
|
||||||
|
if fieldname not in serial_number_fields(self) or not self.get(fieldname):
|
||||||
|
return super().get_formatted(fieldname, doc, currency, absolute_value, translated, format)
|
||||||
|
|
||||||
print_doc = deepcopy(args["doc"])
|
names = self.get(fieldname).split("\n")
|
||||||
set_serial_number_labels(print_doc)
|
labels = {}
|
||||||
return render_body(template, {**args, "doc": print_doc}, **kwargs)
|
if fieldname not in (self.get("__serial_batch_input") or []):
|
||||||
|
labels = self.get("__serial_number_labels") or {}
|
||||||
|
if any(name not in labels for name in names):
|
||||||
|
set_serial_number_labels(self.parent_doc or self)
|
||||||
|
labels = self.get("__serial_number_labels") or {}
|
||||||
|
|
||||||
|
print_row = copy(self)
|
||||||
|
print_row.set(fieldname, "\n".join(escape_html(labels.get(name, name)) for name in names))
|
||||||
|
return super(SerialBatchReference, print_row).get_formatted(
|
||||||
|
fieldname, doc, currency, absolute_value, translated, format
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def set_serial_number_labels(doc):
|
def set_serial_number_labels(doc, method=None, print_settings=None):
|
||||||
rows = [doc, *doc.get_all_children()]
|
rows = [row for row in [doc, *doc.get_all_children()] if isinstance(row, SerialBatchReference)]
|
||||||
fields = ("serial_no", "rejected_serial_no", "current_serial_no")
|
names = {
|
||||||
serial_rows = [
|
name
|
||||||
row
|
|
||||||
for row in rows
|
for row in rows
|
||||||
if row.doctype != "Serial No" and (row.get("item_code") or row.get("rm_item_code"))
|
for field in serial_number_fields(row)
|
||||||
|
if row.get(field) and field not in (row.get("__serial_batch_input") or [])
|
||||||
|
for name in row.get(field).split("\n")
|
||||||
|
}
|
||||||
|
labels = SerialBatchIdentity("Serial No").labels(names)
|
||||||
|
labels = {name: labels.get(name) or name for name in names}
|
||||||
|
for row in rows:
|
||||||
|
row.__dict__["__serial_number_labels"] = labels
|
||||||
|
|
||||||
|
|
||||||
|
def serial_number_fields(row):
|
||||||
|
return [
|
||||||
|
field
|
||||||
|
for field in ("serial_no", "rejected_serial_no", "current_serial_no")
|
||||||
|
if (meta := row.meta.get_field(field)) and meta.fieldtype in ("Small Text", "Text", "Long Text")
|
||||||
]
|
]
|
||||||
values = []
|
|
||||||
for row in serial_rows:
|
|
||||||
for field in fields:
|
|
||||||
meta = row.meta.get_field(field)
|
|
||||||
if meta and meta.fieldtype in ("Small Text", "Text", "Long Text") and row.get(field):
|
|
||||||
values.append((row, field, row.get(field).split("\n")))
|
|
||||||
labels = SerialBatchIdentity("Serial No").labels([name for _, _, names in values for name in names])
|
|
||||||
for row, field, names in values:
|
|
||||||
row.set(field, "\n".join(escape_html(labels.get(name, name)) for name in names))
|
|
||||||
|
|||||||
129
erpnext/stock/tests/test_serial_batch_print.py
Normal file
129
erpnext/stock/tests/test_serial_batch_print.py
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.utils import escape_html
|
||||||
|
from frappe.utils.print_format_generator import PrintFormatGenerator
|
||||||
|
from frappe.www.printview import set_link_titles
|
||||||
|
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||||
|
from erpnext.stock.serial_batch_identity import SerialBatchIdentity
|
||||||
|
from erpnext.tests.utils import ERPNextTestSuite
|
||||||
|
|
||||||
|
|
||||||
|
class TestSerialBatchPrint(ERPNextTestSuite):
|
||||||
|
def make_print(self, number="PRINT-SERIAL"):
|
||||||
|
item = make_item(properties={"has_serial_no": 1, "has_batch_no": 1})
|
||||||
|
serial = SerialBatchIdentity("Serial No").resolve(item.name, [number], create=True)[0]
|
||||||
|
batch = SerialBatchIdentity("Batch").resolve(item.name, ["PRINT-BATCH"], create=True)[0]
|
||||||
|
doc = make_purchase_receipt(
|
||||||
|
item_code=item.name,
|
||||||
|
qty=1,
|
||||||
|
rate=100,
|
||||||
|
use_serial_batch_fields=1,
|
||||||
|
serial_no=serial,
|
||||||
|
batch_no=batch,
|
||||||
|
do_not_submit=True,
|
||||||
|
)
|
||||||
|
table = {
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"fieldname": "items",
|
||||||
|
"table_columns": [
|
||||||
|
{"fieldname": "serial_no", "fieldtype": "Small Text", "label": "Serial No", "width": 50},
|
||||||
|
{
|
||||||
|
"fieldname": "batch_no",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Batch",
|
||||||
|
"label": "Batch No",
|
||||||
|
"width": 50,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
print_format = frappe.new_doc("Print Format")
|
||||||
|
print_format.update(
|
||||||
|
{
|
||||||
|
"doc_type": "Purchase Receipt",
|
||||||
|
"print_format_builder_beta": 1,
|
||||||
|
"pdf_generator": "chrome",
|
||||||
|
"format_data": frappe.as_json({"sections": [{"columns": [{"fields": [table]}]}]}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
set_link_titles(doc)
|
||||||
|
return doc, print_format
|
||||||
|
|
||||||
|
def test_builder_preview_displays_physical_numbers(self):
|
||||||
|
doc, print_format = self.make_print()
|
||||||
|
serial, batch = doc.items[0].serial_no, doc.items[0].batch_no
|
||||||
|
for _ in range(2):
|
||||||
|
html = PrintFormatGenerator(print_format, doc).get_html_preview()
|
||||||
|
self.assertIn("PRINT-SERIAL", html)
|
||||||
|
self.assertIn("PRINT-BATCH", html)
|
||||||
|
self.assertNotIn(serial, html)
|
||||||
|
self.assertNotIn(batch, html)
|
||||||
|
self.assertEqual(doc.items[0].serial_no, serial)
|
||||||
|
self.assertEqual(doc.items[0].batch_no, batch)
|
||||||
|
self.assertEqual(doc.as_dict()["items"][0]["serial_no"], serial)
|
||||||
|
|
||||||
|
def test_builder_pdf_receives_physical_numbers(self):
|
||||||
|
doc, print_format = self.make_print()
|
||||||
|
serial = doc.items[0].serial_no
|
||||||
|
with patch("frappe.utils.pdf.get_chrome_pdf", return_value=b"pdf") as render_pdf:
|
||||||
|
self.assertEqual(PrintFormatGenerator(print_format, doc).render_pdf(), b"pdf")
|
||||||
|
html = render_pdf.call_args.kwargs["html"]
|
||||||
|
self.assertIn("PRINT-SERIAL", html)
|
||||||
|
self.assertNotIn(serial, html)
|
||||||
|
self.assertEqual(doc.items[0].serial_no, serial)
|
||||||
|
|
||||||
|
def test_builder_canvas_displays_physical_numbers(self):
|
||||||
|
from frappe.utils.print_format_generator import get_formatted_field_values
|
||||||
|
|
||||||
|
doc, _ = self.make_print()
|
||||||
|
values = get_formatted_field_values(doc.doctype, doc.name)
|
||||||
|
self.assertEqual(values["child"]["items"][0]["serial_no"], "PRINT-SERIAL")
|
||||||
|
self.assertEqual(values["child"]["items"][0]["batch_no"], "PRINT-BATCH")
|
||||||
|
self.assertEqual(frappe.get_doc(doc.doctype, doc.name).items[0].serial_no, doc.items[0].serial_no)
|
||||||
|
|
||||||
|
def test_print_escapes_physical_serial_numbers(self):
|
||||||
|
number = "PRINT-<img src=x onerror=alert(1)>"
|
||||||
|
doc, print_format = self.make_print(number)
|
||||||
|
serial = doc.items[0].serial_no
|
||||||
|
html = PrintFormatGenerator(print_format, doc).get_html_preview()
|
||||||
|
self.assertIn(escape_html(number), html)
|
||||||
|
self.assertNotIn(number, html)
|
||||||
|
self.assertEqual(doc.items[0].serial_no, serial)
|
||||||
|
|
||||||
|
def test_print_preserves_pending_physical_input(self):
|
||||||
|
doc, print_format = self.make_print()
|
||||||
|
row = doc.items[0]
|
||||||
|
serial = row.serial_no
|
||||||
|
row.__dict__["__serial_batch_input"] = ["serial_no"]
|
||||||
|
html = PrintFormatGenerator(print_format, doc).get_html_preview()
|
||||||
|
self.assertIn(serial, html)
|
||||||
|
self.assertNotIn("PRINT-SERIAL", html)
|
||||||
|
self.assertEqual(row.serial_no, serial)
|
||||||
|
self.assertEqual(row.get("__serial_batch_input"), ["serial_no"])
|
||||||
|
|
||||||
|
def test_formatting_fetches_serial_labels_for_all_rows_together(self):
|
||||||
|
doc, _ = self.make_print()
|
||||||
|
serial = doc.items[0].serial_no
|
||||||
|
other_serial = SerialBatchIdentity("Serial No").resolve(
|
||||||
|
doc.items[0].item_code, ["OTHER-PRINT-SERIAL"], create=True
|
||||||
|
)[0]
|
||||||
|
doc.append("items", {"item_code": doc.items[0].item_code, "serial_no": other_serial})
|
||||||
|
with patch.object(
|
||||||
|
SerialBatchIdentity, "labels", autospec=True, side_effect=SerialBatchIdentity.labels
|
||||||
|
) as labels:
|
||||||
|
self.assertEqual(doc.items[0].get_formatted("serial_no"), "PRINT-SERIAL")
|
||||||
|
self.assertEqual(doc.items[1].get_formatted("serial_no"), "OTHER-PRINT-SERIAL")
|
||||||
|
self.assertEqual(doc.items[0].get_formatted("serial_no"), "PRINT-SERIAL")
|
||||||
|
self.assertEqual(labels.call_count, 1)
|
||||||
|
self.assertEqual(set(labels.call_args.args[1]), {serial, other_serial})
|
||||||
|
self.assertNotIn("__serial_number_labels", doc.as_dict()["items"][0])
|
||||||
|
|
||||||
|
def test_custom_print_can_still_look_up_serial_by_id(self):
|
||||||
|
doc, _ = self.make_print()
|
||||||
|
template = (
|
||||||
|
"{{ frappe.db.get_value('Serial No', doc.items[0].serial_no, 'serial_no') }} / "
|
||||||
|
"{{ doc.items[0].get_formatted('serial_no') }}"
|
||||||
|
)
|
||||||
|
self.assertEqual(frappe.render_template(template, {"doc": doc}), "PRINT-SERIAL / PRINT-SERIAL")
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class SubcontractingReceiptItem(Document):
|
class SubcontractingReceiptItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from erpnext.stock.serial_batch_display import SerialBatchReference
|
||||||
|
|
||||||
|
|
||||||
class SubcontractingReceiptSuppliedItem(Document):
|
class SubcontractingReceiptSuppliedItem(SerialBatchReference):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
# This code is auto-generated. Do not modify anything in this block.
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user