Merge branch 'develop' into validate-item-price-uom

This commit is contained in:
Raffael Meyer
2024-03-25 14:25:54 +01:00
committed by GitHub
75 changed files with 845 additions and 271 deletions

View File

@@ -185,7 +185,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Address", "label": "Address",
"read_only": 1 "read_only": 1
}, },
@@ -204,7 +204,7 @@
}, },
{ {
"fieldname": "company_address_display", "fieldname": "company_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Company Address Display", "label": "Company Address Display",
"read_only": 1 "read_only": 1
}, },
@@ -381,7 +381,7 @@
], ],
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2023-06-15 15:46:53.865712", "modified": "2024-03-22 16:01:13.231067",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Dunning", "name": "Dunning",

View File

@@ -32,14 +32,14 @@ class Dunning(AccountsController):
from erpnext.accounts.doctype.overdue_payment.overdue_payment import OverduePayment from erpnext.accounts.doctype.overdue_payment.overdue_payment import OverduePayment
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
base_dunning_amount: DF.Currency base_dunning_amount: DF.Currency
body_text: DF.TextEditor | None body_text: DF.TextEditor | None
closing_text: DF.TextEditor | None closing_text: DF.TextEditor | None
company: DF.Link company: DF.Link
company_address: DF.Link | None company_address: DF.Link | None
company_address_display: DF.SmallText | None company_address_display: DF.TextEditor | None
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
contact_email: DF.Data | None contact_email: DF.Data | None
contact_mobile: DF.SmallText | None contact_mobile: DF.SmallText | None

View File

@@ -57,13 +57,13 @@ frappe.ui.form.on("Exchange Rate Revaluation", {
get_entries: function (frm, account) { get_entries: function (frm, account) {
frappe.call({ frappe.call({
method: "get_accounts_data", method: "get_accounts_data",
doc: cur_frm.doc, doc: frm.doc,
account: account, account: account,
callback: function (r) { callback: function (r) {
frappe.model.clear_table(frm.doc, "accounts"); frappe.model.clear_table(frm.doc, "accounts");
if (r.message) { if (r.message) {
r.message.forEach((d) => { r.message.forEach((d) => {
cur_frm.add_child("accounts", d); frm.add_child("accounts", d);
}); });
frm.events.get_total_gain_loss(frm); frm.events.get_total_gain_loss(frm);
refresh_field("accounts"); refresh_field("accounts");

View File

@@ -189,7 +189,7 @@ frappe.ui.form.on("Invoice Discounting", {
show_general_ledger: (frm) => { show_general_ledger: (frm) => {
if (frm.doc.docstatus > 0) { if (frm.doc.docstatus > 0) {
cur_frm.add_custom_button( frm.add_custom_button(
__("Accounting Ledger"), __("Accounting Ledger"),
function () { function () {
frappe.route_options = { frappe.route_options = {

View File

@@ -255,7 +255,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
} }
onload_post_render() { onload_post_render() {
cur_frm.get_field("accounts").grid.set_multiple_add("account"); this.frm.get_field("accounts").grid.set_multiple_add("account");
} }
load_defaults() { load_defaults() {
@@ -402,7 +402,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
row.debit = -doc.difference; row.debit = -doc.difference;
} }
} }
cur_frm.cscript.update_totals(doc); this.frm.cscript.update_totals(doc);
erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, "accounts"); erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, "accounts");
} }
@@ -469,11 +469,11 @@ frappe.ui.form.on("Journal Entry Account", {
}, },
debit: function (frm, dt, dn) { debit: function (frm, dt, dn) {
cur_frm.cscript.update_totals(frm.doc); frm.cscript.update_totals(frm.doc);
}, },
credit: function (frm, dt, dn) { credit: function (frm, dt, dn) {
cur_frm.cscript.update_totals(frm.doc); frm.cscript.update_totals(frm.doc);
}, },
exchange_rate: function (frm, cdt, cdn) { exchange_rate: function (frm, cdt, cdn) {
@@ -489,7 +489,7 @@ frappe.ui.form.on("Journal Entry Account", {
}); });
frappe.ui.form.on("Journal Entry Account", "accounts_remove", function (frm) { frappe.ui.form.on("Journal Entry Account", "accounts_remove", function (frm) {
cur_frm.cscript.update_totals(frm.doc); frm.cscript.update_totals(frm.doc);
}); });
$.extend(erpnext.journal_entry, { $.extend(erpnext.journal_entry, {
@@ -531,7 +531,7 @@ $.extend(erpnext.journal_entry, {
flt(flt(row.credit_in_account_currency) * row.exchange_rate, precision("credit", row)) flt(flt(row.credit_in_account_currency) * row.exchange_rate, precision("credit", row))
); );
cur_frm.cscript.update_totals(frm.doc); frm.cscript.update_totals(frm.doc);
}, },
set_exchange_rate: function (frm, cdt, cdn) { set_exchange_rate: function (frm, cdt, cdn) {
@@ -673,10 +673,10 @@ $.extend(erpnext.journal_entry, {
return { filters: filters }; return { filters: filters };
}, },
reverse_journal_entry: function () { reverse_journal_entry: function (frm) {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry", method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry",
frm: cur_frm, frm: frm,
}); });
}, },
}); });

View File

@@ -322,13 +322,13 @@ frappe.ui.form.on("Payment Entry", {
"references" "references"
); );
cur_frm.set_df_property( frm.set_df_property(
"source_exchange_rate", "source_exchange_rate",
"description", "description",
"1 " + frm.doc.paid_from_account_currency + " = [?] " + company_currency "1 " + frm.doc.paid_from_account_currency + " = [?] " + company_currency
); );
cur_frm.set_df_property( frm.set_df_property(
"target_exchange_rate", "target_exchange_rate",
"description", "description",
"1 " + frm.doc.paid_to_account_currency + " = [?] " + company_currency "1 " + frm.doc.paid_to_account_currency + " = [?] " + company_currency

View File

@@ -193,7 +193,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex
make_sales_return() { make_sales_return() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.pos_invoice.pos_invoice.make_sales_return", method: "erpnext.accounts.doctype.pos_invoice.pos_invoice.make_sales_return",
frm: cur_frm, frm: this.frm,
}); });
} }
}; };
@@ -293,7 +293,7 @@ frappe.ui.form.on("POS Invoice", {
}); });
} else if (frappe.dom.freeze_count != 0) { } else if (frappe.dom.freeze_count != 0) {
frappe.dom.unfreeze(); frappe.dom.unfreeze();
cur_frm.reload_doc(); frm.reload_doc();
cur_pos.payment.events.submit_invoice(); cur_pos.payment.events.submit_invoice();
frappe.show_alert({ frappe.show_alert({

View File

@@ -420,7 +420,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Address", "label": "Address",
"read_only": 1 "read_only": 1
}, },
@@ -475,7 +475,7 @@
}, },
{ {
"fieldname": "shipping_address", "fieldname": "shipping_address",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Shipping Address", "label": "Shipping Address",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
@@ -489,7 +489,7 @@
}, },
{ {
"fieldname": "company_address_display", "fieldname": "company_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hidden": 1, "hidden": 1,
"label": "Company Address", "label": "Company Address",
"print_hide": 1, "print_hide": 1,
@@ -1563,7 +1563,7 @@
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-03-20 16:00:34.268756", "modified": "2024-03-22 16:15:08.561034",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "POS Invoice", "name": "POS Invoice",

View File

@@ -47,7 +47,7 @@ class POSInvoice(SalesInvoice):
account_for_change_amount: DF.Link | None account_for_change_amount: DF.Link | None
additional_discount_percentage: DF.Float additional_discount_percentage: DF.Float
address_display: DF.SmallText | None address_display: DF.TextEditor | None
advances: DF.Table[SalesInvoiceAdvance] advances: DF.Table[SalesInvoiceAdvance]
against_income_account: DF.SmallText | None against_income_account: DF.SmallText | None
allocate_advances_automatically: DF.Check allocate_advances_automatically: DF.Check
@@ -72,7 +72,7 @@ class POSInvoice(SalesInvoice):
commission_rate: DF.Float commission_rate: DF.Float
company: DF.Link company: DF.Link
company_address: DF.Link | None company_address: DF.Link | None
company_address_display: DF.SmallText | None company_address_display: DF.TextEditor | None
consolidated_invoice: DF.Link | None consolidated_invoice: DF.Link | None
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
contact_email: DF.Data | None contact_email: DF.Data | None
@@ -138,7 +138,7 @@ class POSInvoice(SalesInvoice):
selling_price_list: DF.Link selling_price_list: DF.Link
set_posting_time: DF.Check set_posting_time: DF.Check
set_warehouse: DF.Link | None set_warehouse: DF.Link | None
shipping_address: DF.SmallText | None shipping_address: DF.TextEditor | None
shipping_address_name: DF.Link | None shipping_address_name: DF.Link | None
shipping_rule: DF.Link | None shipping_rule: DF.Link | None
source: DF.Link | None source: DF.Link | None

View File

@@ -736,7 +736,6 @@ def get_pricing_rule_items(pr_doc, other_items=False) -> list:
def validate_coupon_code(coupon_name): def validate_coupon_code(coupon_name):
coupon = frappe.get_doc("Coupon Code", coupon_name) coupon = frappe.get_doc("Coupon Code", coupon_name)
if coupon.valid_from: if coupon.valid_from:
if coupon.valid_from > getdate(today()): if coupon.valid_from > getdate(today()):
frappe.throw(_("Sorry, this coupon code's validity has not started")) frappe.throw(_("Sorry, this coupon code's validity has not started"))

View File

@@ -131,17 +131,17 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
if (doc.docstatus == 1 && doc.outstanding_amount != 0 && !doc.on_hold) { if (doc.docstatus == 1 && doc.outstanding_amount != 0 && !doc.on_hold) {
this.frm.add_custom_button(__("Payment"), () => this.make_payment_entry(), __("Create")); this.frm.add_custom_button(__("Payment"), () => this.make_payment_entry(), __("Create"));
cur_frm.page.set_inner_btn_group_as_primary(__("Create")); this.frm.page.set_inner_btn_group_as_primary(__("Create"));
} }
if (!doc.is_return && doc.docstatus == 1) { if (!doc.is_return && doc.docstatus == 1) {
if (doc.outstanding_amount >= 0 || Math.abs(flt(doc.outstanding_amount)) < flt(doc.grand_total)) { if (doc.outstanding_amount >= 0 || Math.abs(flt(doc.outstanding_amount)) < flt(doc.grand_total)) {
cur_frm.add_custom_button(__("Return / Debit Note"), this.make_debit_note, __("Create")); this.frm.add_custom_button(__("Return / Debit Note"), this.make_debit_note, __("Create"));
} }
} }
if (doc.outstanding_amount > 0 && !cint(doc.is_return) && !doc.on_hold) { if (doc.outstanding_amount > 0 && !cint(doc.is_return) && !doc.on_hold) {
cur_frm.add_custom_button( this.frm.add_custom_button(
__("Payment Request"), __("Payment Request"),
function () { function () {
me.make_payment_request(); me.make_payment_request();
@@ -462,7 +462,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
make_debit_note() { make_debit_note() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_debit_note", method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_debit_note",
frm: cur_frm, frm: this.frm,
}); });
} }
}; };

View File

@@ -443,7 +443,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Address", "label": "Address",
"read_only": 1 "read_only": 1
}, },
@@ -489,7 +489,7 @@
}, },
{ {
"fieldname": "shipping_address_display", "fieldname": "shipping_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Shipping Address", "label": "Shipping Address",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
@@ -1363,7 +1363,7 @@
}, },
{ {
"fieldname": "billing_address_display", "fieldname": "billing_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Billing Address", "label": "Billing Address",
"read_only": 1 "read_only": 1
}, },
@@ -1638,7 +1638,7 @@
"idx": 204, "idx": 204,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-03-20 15:57:00.736868", "modified": "2024-03-22 16:15:09.099187",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@@ -82,7 +82,7 @@ class PurchaseInvoice(BuyingController):
) )
additional_discount_percentage: DF.Float additional_discount_percentage: DF.Float
address_display: DF.SmallText | None address_display: DF.TextEditor | None
advance_tax: DF.Table[AdvanceTax] advance_tax: DF.Table[AdvanceTax]
advances: DF.Table[PurchaseInvoiceAdvance] advances: DF.Table[PurchaseInvoiceAdvance]
against_expense_account: DF.SmallText | None against_expense_account: DF.SmallText | None
@@ -107,7 +107,7 @@ class PurchaseInvoice(BuyingController):
bill_date: DF.Date | None bill_date: DF.Date | None
bill_no: DF.Data | None bill_no: DF.Data | None
billing_address: DF.Link | None billing_address: DF.Link | None
billing_address_display: DF.SmallText | None billing_address_display: DF.TextEditor | None
buying_price_list: DF.Link | None buying_price_list: DF.Link | None
cash_bank_account: DF.Link | None cash_bank_account: DF.Link | None
clearance_date: DF.Date | None clearance_date: DF.Date | None
@@ -174,7 +174,7 @@ class PurchaseInvoice(BuyingController):
set_posting_time: DF.Check set_posting_time: DF.Check
set_warehouse: DF.Link | None set_warehouse: DF.Link | None
shipping_address: DF.Link | None shipping_address: DF.Link | None
shipping_address_display: DF.SmallText | None shipping_address_display: DF.TextEditor | None
shipping_rule: DF.Link | None shipping_rule: DF.Link | None
status: DF.Literal[ status: DF.Literal[
"", "",

View File

@@ -61,9 +61,9 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
refresh(doc, dt, dn) { refresh(doc, dt, dn) {
const me = this; const me = this;
super.refresh(); super.refresh();
if (cur_frm.msgbox && cur_frm.msgbox.$wrapper.is(":visible")) { if (this.frm.msgbox && this.frm.msgbox.$wrapper.is(":visible")) {
// hide new msgbox // hide new msgbox
cur_frm.msgbox.hide(); this.frm.msgbox.hide();
} }
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return); this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
@@ -113,33 +113,33 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
if (doc.docstatus == 1 && !doc.is_return) { if (doc.docstatus == 1 && !doc.is_return) {
var is_delivered_by_supplier = false; var is_delivered_by_supplier = false;
is_delivered_by_supplier = cur_frm.doc.items.some(function (item) { is_delivered_by_supplier = this.frm.doc.items.some(function (item) {
return item.is_delivered_by_supplier ? true : false; return item.is_delivered_by_supplier ? true : false;
}); });
if (doc.outstanding_amount >= 0 || Math.abs(flt(doc.outstanding_amount)) < flt(doc.grand_total)) { if (doc.outstanding_amount >= 0 || Math.abs(flt(doc.outstanding_amount)) < flt(doc.grand_total)) {
cur_frm.add_custom_button(__("Return / Credit Note"), this.make_sales_return, __("Create")); this.frm.add_custom_button(__("Return / Credit Note"), this.make_sales_return, __("Create"));
cur_frm.page.set_inner_btn_group_as_primary(__("Create")); this.frm.page.set_inner_btn_group_as_primary(__("Create"));
} }
if (cint(doc.update_stock) != 1) { if (cint(doc.update_stock) != 1) {
// show Make Delivery Note button only if Sales Invoice is not created from Delivery Note // show Make Delivery Note button only if Sales Invoice is not created from Delivery Note
var from_delivery_note = false; var from_delivery_note = false;
from_delivery_note = cur_frm.doc.items.some(function (item) { from_delivery_note = this.frm.doc.items.some(function (item) {
return item.delivery_note ? true : false; return item.delivery_note ? true : false;
}); });
if (!from_delivery_note && !is_delivered_by_supplier) { if (!from_delivery_note && !is_delivered_by_supplier) {
cur_frm.add_custom_button( this.frm.add_custom_button(
__("Delivery"), __("Delivery"),
cur_frm.cscript["Make Delivery Note"], this.frm.cscript["Make Delivery Note"],
__("Create") __("Create")
); );
} }
} }
if (doc.outstanding_amount > 0) { if (doc.outstanding_amount > 0) {
cur_frm.add_custom_button( this.frm.add_custom_button(
__("Payment Request"), __("Payment Request"),
function () { function () {
me.make_payment_request(); me.make_payment_request();
@@ -147,10 +147,10 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
__("Create") __("Create")
); );
cur_frm.add_custom_button( this.frm.add_custom_button(
__("Invoice Discounting"), __("Invoice Discounting"),
function () { function () {
cur_frm.events.create_invoice_discounting(cur_frm); this.frm.events.create_invoice_discounting(this.frm);
}, },
__("Create") __("Create")
); );
@@ -171,10 +171,10 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
} }
if (doc.docstatus === 1) { if (doc.docstatus === 1) {
cur_frm.add_custom_button( this.frm.add_custom_button(
__("Maintenance Schedule"), __("Maintenance Schedule"),
function () { function () {
cur_frm.cscript.make_maintenance_schedule(); this.frm.cscript.make_maintenance_schedule();
}, },
__("Create") __("Create")
); );
@@ -182,7 +182,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
} }
// Show buttons only when pos view is active // Show buttons only when pos view is active
if (cint(doc.docstatus == 0) && cur_frm.page.current_view_name !== "pos" && !doc.is_return) { if (cint(doc.docstatus == 0) && this.frm.page.current_view_name !== "pos" && !doc.is_return) {
this.frm.cscript.sales_order_btn(); this.frm.cscript.sales_order_btn();
this.frm.cscript.delivery_note_btn(); this.frm.cscript.delivery_note_btn();
this.frm.cscript.quotation_btn(); this.frm.cscript.quotation_btn();
@@ -213,7 +213,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
make_maintenance_schedule() { make_maintenance_schedule() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_maintenance_schedule", method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_maintenance_schedule",
frm: cur_frm, frm: this.frm,
}); });
} }
@@ -232,28 +232,27 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
set_default_print_format() { set_default_print_format() {
// set default print format to POS type or Credit Note // set default print format to POS type or Credit Note
if (cur_frm.doc.is_pos) { if (this.frm.doc.is_pos) {
if (cur_frm.pos_print_format) { if (this.frm.pos_print_format) {
cur_frm.meta._default_print_format = cur_frm.meta.default_print_format; this.frm.meta._default_print_format = this.frm.meta.default_print_format;
cur_frm.meta.default_print_format = cur_frm.pos_print_format; this.frm.meta.default_print_format = this.frm.pos_print_format;
} }
} else if (cur_frm.doc.is_return && !cur_frm.meta.default_print_format) { } else if (this.frm.doc.is_return && !this.frm.meta.default_print_format) {
if (cur_frm.return_print_format) { if (this.frm.return_print_format) {
cur_frm.meta._default_print_format = cur_frm.meta.default_print_format; this.frm.meta._default_print_format = this.frm.meta.default_print_format;
cur_frm.meta.default_print_format = cur_frm.return_print_format; this.frm.meta.default_print_format = this.frm.return_print_format;
} }
} else { } else {
if (cur_frm.meta._default_print_format) { if (this.frm.meta._default_print_format) {
cur_frm.meta.default_print_format = cur_frm.meta._default_print_format; this.frm.meta.default_print_format = this.frm.meta._default_print_format;
cur_frm.meta._default_print_format = null; this.frm.meta._default_print_format = null;
} else if ( } else if (
in_list( [this.frm.pos_print_format, this.frm.return_print_format].includes(
[cur_frm.pos_print_format, cur_frm.return_print_format], this.frm.meta.default_print_format
cur_frm.meta.default_print_format
) )
) { ) {
cur_frm.meta.default_print_format = null; this.frm.meta.default_print_format = null;
cur_frm.meta._default_print_format = null; this.frm.meta._default_print_format = null;
} }
} }
} }
@@ -465,7 +464,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
make_sales_return() { make_sales_return() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return", method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return",
frm: cur_frm, frm: this.frm,
}); });
} }

View File

@@ -90,6 +90,7 @@
"section_break_49", "section_break_49",
"apply_discount_on", "apply_discount_on",
"base_discount_amount", "base_discount_amount",
"coupon_code",
"is_cash_or_non_trade_discount", "is_cash_or_non_trade_discount",
"additional_discount_account", "additional_discount_account",
"column_break_51", "column_break_51",
@@ -494,7 +495,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Address", "label": "Address",
@@ -565,7 +566,7 @@
}, },
{ {
"fieldname": "shipping_address", "fieldname": "shipping_address",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Shipping Address", "label": "Shipping Address",
@@ -583,7 +584,7 @@
}, },
{ {
"fieldname": "company_address_display", "fieldname": "company_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Company Address", "label": "Company Address",
@@ -1998,7 +1999,7 @@
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"fieldname": "dispatch_address", "fieldname": "dispatch_address",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Dispatch Address", "label": "Dispatch Address",
"read_only": 1 "read_only": 1
}, },
@@ -2173,6 +2174,12 @@
"label": "Don't Create Loyalty Points", "label": "Don't Create Loyalty Points",
"no_copy": 1 "no_copy": 1
}, },
{
"fieldname": "coupon_code",
"fieldtype": "Link",
"label": "Coupon Code",
"options": "Coupon Code"
},
{ {
"default": "1", "default": "1",
"depends_on": "eval: doc.is_return && doc.return_against", "depends_on": "eval: doc.is_return && doc.return_against",
@@ -2180,7 +2187,8 @@
"fieldname": "update_outstanding_for_self", "fieldname": "update_outstanding_for_self",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Update Outstanding for Self", "label": "Update Outstanding for Self",
"no_copy": 1 "no_copy": 1,
"print_hide": 1
} }
], ],
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
@@ -2193,7 +2201,7 @@
"link_fieldname": "consolidated_invoice" "link_fieldname": "consolidated_invoice"
} }
], ],
"modified": "2024-03-20 16:02:52.237732", "modified": "2024-03-22 17:50:34.395602",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@@ -16,6 +16,10 @@ from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
get_loyalty_program_details_with_points, get_loyalty_program_details_with_points,
validate_loyalty_points, validate_loyalty_points,
) )
from erpnext.accounts.doctype.pricing_rule.utils import (
update_coupon_code_count,
validate_coupon_code,
)
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import ( from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
validate_docs_for_deferred_accounting, validate_docs_for_deferred_accounting,
validate_docs_for_voucher_types, validate_docs_for_voucher_types,
@@ -73,7 +77,7 @@ class SalesInvoice(SellingController):
account_for_change_amount: DF.Link | None account_for_change_amount: DF.Link | None
additional_discount_account: DF.Link | None additional_discount_account: DF.Link | None
additional_discount_percentage: DF.Float additional_discount_percentage: DF.Float
address_display: DF.SmallText | None address_display: DF.TextEditor | None
advances: DF.Table[SalesInvoiceAdvance] advances: DF.Table[SalesInvoiceAdvance]
against_income_account: DF.SmallText | None against_income_account: DF.SmallText | None
allocate_advances_automatically: DF.Check allocate_advances_automatically: DF.Check
@@ -98,7 +102,7 @@ class SalesInvoice(SellingController):
commission_rate: DF.Float commission_rate: DF.Float
company: DF.Link company: DF.Link
company_address: DF.Link | None company_address: DF.Link | None
company_address_display: DF.SmallText | None company_address_display: DF.TextEditor | None
company_tax_id: DF.Data | None company_tax_id: DF.Data | None
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
contact_email: DF.Data | None contact_email: DF.Data | None
@@ -106,6 +110,7 @@ class SalesInvoice(SellingController):
contact_person: DF.Link | None contact_person: DF.Link | None
conversion_rate: DF.Float conversion_rate: DF.Float
cost_center: DF.Link | None cost_center: DF.Link | None
coupon_code: DF.Link | None
currency: DF.Link currency: DF.Link
customer: DF.Link | None customer: DF.Link | None
customer_address: DF.Link | None customer_address: DF.Link | None
@@ -114,7 +119,7 @@ class SalesInvoice(SellingController):
debit_to: DF.Link debit_to: DF.Link
disable_rounded_total: DF.Check disable_rounded_total: DF.Check
discount_amount: DF.Currency discount_amount: DF.Currency
dispatch_address: DF.SmallText | None dispatch_address: DF.TextEditor | None
dispatch_address_name: DF.Link | None dispatch_address_name: DF.Link | None
dont_create_loyalty_points: DF.Check dont_create_loyalty_points: DF.Check
due_date: DF.Date | None due_date: DF.Date | None
@@ -178,7 +183,7 @@ class SalesInvoice(SellingController):
set_posting_time: DF.Check set_posting_time: DF.Check
set_target_warehouse: DF.Link | None set_target_warehouse: DF.Link | None
set_warehouse: DF.Link | None set_warehouse: DF.Link | None
shipping_address: DF.SmallText | None shipping_address: DF.TextEditor | None
shipping_address_name: DF.Link | None shipping_address_name: DF.Link | None
shipping_rule: DF.Link | None shipping_rule: DF.Link | None
source: DF.Link | None source: DF.Link | None
@@ -294,6 +299,10 @@ class SalesInvoice(SellingController):
self.doctype, self.customer, self.company, self.inter_company_invoice_reference self.doctype, self.customer, self.company, self.inter_company_invoice_reference
) )
# Validating coupon code
if self.coupon_code:
validate_coupon_code(self.coupon_code)
if cint(self.is_pos): if cint(self.is_pos):
self.validate_pos() self.validate_pos()
@@ -473,6 +482,9 @@ class SalesInvoice(SellingController):
self.update_project() self.update_project()
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference) update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
if self.coupon_code:
update_coupon_code_count(self.coupon_code, "used")
# create the loyalty point ledger entry if the customer is enrolled in any loyalty program # create the loyalty point ledger entry if the customer is enrolled in any loyalty program
if ( if (
not self.is_return not self.is_return
@@ -563,6 +575,9 @@ class SalesInvoice(SellingController):
self.db_set("status", "Cancelled") self.db_set("status", "Cancelled")
self.db_set("repost_required", 0) self.db_set("repost_required", 0)
if self.coupon_code:
update_coupon_code_count(self.coupon_code, "cancelled")
if ( if (
frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction" frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction"
): ):

View File

@@ -669,20 +669,20 @@ class GrossProfitGenerator(object):
elif row.sales_order and row.so_detail: elif row.sales_order and row.so_detail:
incoming_amount = self.get_buying_amount_from_so_dn(row.sales_order, row.so_detail, item_code) incoming_amount = self.get_buying_amount_from_so_dn(row.sales_order, row.so_detail, item_code)
if incoming_amount: if incoming_amount:
return incoming_amount return flt(row.qty) * incoming_amount
else: else:
return flt(row.qty) * self.get_average_buying_rate(row, item_code) return flt(row.qty) * self.get_average_buying_rate(row, item_code)
return flt(row.qty) * self.get_average_buying_rate(row, item_code) return flt(row.qty) * self.get_average_buying_rate(row, item_code)
def get_buying_amount_from_so_dn(self, sales_order, so_detail, item_code): def get_buying_amount_from_so_dn(self, sales_order, so_detail, item_code):
from frappe.query_builder.functions import Sum from frappe.query_builder.functions import Avg
delivery_note_item = frappe.qb.DocType("Delivery Note Item") delivery_note_item = frappe.qb.DocType("Delivery Note Item")
query = ( query = (
frappe.qb.from_(delivery_note_item) frappe.qb.from_(delivery_note_item)
.select(Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty)) .select(Avg(delivery_note_item.incoming_rate))
.where(delivery_note_item.docstatus == 1) .where(delivery_note_item.docstatus == 1)
.where(delivery_note_item.item_code == item_code) .where(delivery_note_item.item_code == item_code)
.where(delivery_note_item.against_sales_order == sales_order) .where(delivery_note_item.against_sales_order == sales_order)

View File

@@ -460,3 +460,95 @@ class TestGrossProfit(FrappeTestCase):
} }
gp_entry = [x for x in data if x.parent_invoice == sinv.name] gp_entry = [x for x in data if x.parent_invoice == sinv.name]
self.assertDictContainsSubset(expected_entry, gp_entry[0]) self.assertDictContainsSubset(expected_entry, gp_entry[0])
def test_different_rates_in_si_and_dn(self):
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
"""
Test gp calculation when invoice and delivery note differ in qty and aren't connected
SO -- INV
|
DN
"""
se = make_stock_entry(
company=self.company,
item_code=self.item,
target=self.warehouse,
qty=3,
basic_rate=700,
do_not_submit=True,
)
item = se.items[0]
se.append(
"items",
{
"item_code": item.item_code,
"s_warehouse": item.s_warehouse,
"t_warehouse": item.t_warehouse,
"qty": 10,
"basic_rate": 700,
"conversion_factor": item.conversion_factor or 1.0,
"transfer_qty": flt(item.qty) * (flt(item.conversion_factor) or 1.0),
"serial_no": item.serial_no,
"batch_no": item.batch_no,
"cost_center": item.cost_center,
"expense_account": item.expense_account,
},
)
se = se.save().submit()
so = make_sales_order(
customer=self.customer,
company=self.company,
warehouse=self.warehouse,
item=self.item,
rate=800,
qty=10,
do_not_save=False,
do_not_submit=False,
)
from erpnext.selling.doctype.sales_order.sales_order import (
make_delivery_note,
make_sales_invoice,
)
dn1 = make_delivery_note(so.name)
dn1.items[0].qty = 4
dn1.items[0].rate = 800
dn1.save().submit()
dn2 = make_delivery_note(so.name)
dn2.items[0].qty = 6
dn2.items[0].rate = 800
dn2.save().submit()
sinv = make_sales_invoice(so.name)
sinv.items[0].qty = 4
sinv.items[0].rate = 800
sinv.save().submit()
filters = frappe._dict(
company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice"
)
columns, data = execute(filters=filters)
expected_entry = {
"parent_invoice": sinv.name,
"currency": "INR",
"sales_invoice": self.item,
"customer": self.customer,
"posting_date": frappe.utils.datetime.date.fromisoformat(nowdate()),
"item_code": self.item,
"item_name": self.item,
"warehouse": "Stores - _GP",
"qty": 4.0,
"avg._selling_rate": 800.0,
"valuation_rate": 700.0,
"selling_amount": 3200.0,
"buying_amount": 2800.0,
"gross_profit": 400.0,
"gross_profit_%": 12.5,
}
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
self.assertDictContainsSubset(expected_entry, gp_entry[0])

View File

@@ -24,3 +24,10 @@ frappe.query_reports["Profit and Loss Statement"]["filters"].push({
fieldtype: "Check", fieldtype: "Check",
default: 1, default: 1,
}); });
frappe.query_reports["Profit and Loss Statement"]["filters"].push({
fieldname: "include_default_book_entries",
label: __("Include Default FB Entries"),
fieldtype: "Check",
default: 1,
});

View File

@@ -48,7 +48,7 @@ frappe.ui.form.on("Asset", {
method: "erpnext.assets.doctype.asset.asset.make_asset_movement", method: "erpnext.assets.doctype.asset.asset.make_asset_movement",
freeze: true, freeze: true,
args: { args: {
assets: [{ name: cur_frm.doc.name }], assets: [{ name: frm.doc.name }],
}, },
callback: function (r) { callback: function (r) {
if (r.message) { if (r.message) {
@@ -791,9 +791,7 @@ erpnext.asset.scrap_asset = function (frm) {
asset_name: frm.doc.name, asset_name: frm.doc.name,
}, },
method: "erpnext.assets.doctype.asset.depreciation.scrap_asset", method: "erpnext.assets.doctype.asset.depreciation.scrap_asset",
callback: function (r) { callback: (r) => frm.reload_doc(),
cur_frm.reload_doc();
},
}); });
}); });
}; };
@@ -805,19 +803,17 @@ erpnext.asset.restore_asset = function (frm) {
asset_name: frm.doc.name, asset_name: frm.doc.name,
}, },
method: "erpnext.assets.doctype.asset.depreciation.restore_asset", method: "erpnext.assets.doctype.asset.depreciation.restore_asset",
callback: function (r) { callback: (r) => frm.reload_doc(),
cur_frm.reload_doc();
},
}); });
}); });
}; };
erpnext.asset.transfer_asset = function () { erpnext.asset.transfer_asset = function (frm) {
frappe.call({ frappe.call({
method: "erpnext.assets.doctype.asset.asset.make_asset_movement", method: "erpnext.assets.doctype.asset.asset.make_asset_movement",
freeze: true, freeze: true,
args: { args: {
assets: [{ name: cur_frm.doc.name }], assets: [{ name: frm.doc.name }],
purpose: "Transfer", purpose: "Transfer",
}, },
callback: function (r) { callback: function (r) {

View File

@@ -86,7 +86,7 @@ frappe.ui.form.on("Purchase Order", {
args: { args: {
subcontract_order: frm.doc.name, subcontract_order: frm.doc.name,
rm_details: po_details, rm_details: po_details,
order_doctype: cur_frm.doc.doctype, order_doctype: frm.doc.doctype,
}, },
callback: function (r) { callback: function (r) {
if (r && r.message) { if (r && r.message) {
@@ -270,8 +270,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
var allow_receipt = false; var allow_receipt = false;
var is_drop_ship = false; var is_drop_ship = false;
for (var i in cur_frm.doc.items) { for (var i in this.frm.doc.items) {
var item = cur_frm.doc.items[i]; var item = this.frm.doc.items[i];
if (item.delivered_by_supplier !== 1) { if (item.delivered_by_supplier !== 1) {
allow_receipt = true; allow_receipt = true;
} else { } else {
@@ -348,7 +348,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
if (doc.status != "Closed") { if (doc.status != "Closed") {
if (doc.status != "On Hold") { if (doc.status != "On Hold") {
if (flt(doc.per_received, 2) < 100 && allow_receipt) { if (flt(doc.per_received, 2) < 100 && allow_receipt) {
cur_frm.add_custom_button( this.frm.add_custom_button(
__("Purchase Receipt"), __("Purchase Receipt"),
this.make_purchase_receipt, this.make_purchase_receipt,
__("Create") __("Create")
@@ -356,7 +356,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
if (doc.is_subcontracted) { if (doc.is_subcontracted) {
if (doc.is_old_subcontracting_flow) { if (doc.is_old_subcontracting_flow) {
if (me.has_unsupplied_items()) { if (me.has_unsupplied_items()) {
cur_frm.add_custom_button( this.frm.add_custom_button(
__("Material to Supplier"), __("Material to Supplier"),
function () { function () {
me.make_stock_entry(); me.make_stock_entry();
@@ -365,7 +365,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
); );
} }
} else { } else {
cur_frm.add_custom_button( this.frm.add_custom_button(
__("Subcontracting Order"), __("Subcontracting Order"),
this.make_subcontracting_order, this.make_subcontracting_order,
__("Create") __("Create")
@@ -374,7 +374,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
} }
} }
if (flt(doc.per_billed, 2) < 100) if (flt(doc.per_billed, 2) < 100)
cur_frm.add_custom_button( this.frm.add_custom_button(
__("Purchase Invoice"), __("Purchase Invoice"),
this.make_purchase_invoice, this.make_purchase_invoice,
__("Create") __("Create")
@@ -418,10 +418,10 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
} }
} }
cur_frm.page.set_inner_btn_group_as_primary(__("Create")); this.frm.page.set_inner_btn_group_as_primary(__("Create"));
} }
} else if (doc.docstatus === 0) { } else if (doc.docstatus === 0) {
cur_frm.cscript.add_from_mappers(); this.frm.cscript.add_from_mappers();
} }
} }
@@ -458,8 +458,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
frappe.call({ frappe.call({
method: "erpnext.controllers.subcontracting_controller.make_rm_stock_entry", method: "erpnext.controllers.subcontracting_controller.make_rm_stock_entry",
args: { args: {
subcontract_order: cur_frm.doc.name, subcontract_order: this.frm.doc.name,
order_doctype: cur_frm.doc.doctype, order_doctype: this.frm.doc.doctype,
}, },
callback: function (r) { callback: function (r) {
var doclist = frappe.model.sync(r.message); var doclist = frappe.model.sync(r.message);
@@ -478,7 +478,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
make_purchase_receipt() { make_purchase_receipt() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt", method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt",
frm: cur_frm, frm: this.frm,
freeze_message: __("Creating Purchase Receipt ..."), freeze_message: __("Creating Purchase Receipt ..."),
}); });
} }
@@ -486,14 +486,14 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
make_purchase_invoice() { make_purchase_invoice() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice", method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice",
frm: cur_frm, frm: this.frm,
}); });
} }
make_subcontracting_order() { make_subcontracting_order() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_subcontracting_order", method: "erpnext.buying.doctype.purchase_order.purchase_order.make_subcontracting_order",
frm: cur_frm, frm: this.frm,
freeze_message: __("Creating Subcontracting Order ..."), freeze_message: __("Creating Subcontracting Order ..."),
}); });
} }
@@ -652,7 +652,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
} }
unhold_purchase_order() { unhold_purchase_order() {
cur_frm.cscript.update_status("Resume", "Draft"); this.frm.cscript.update_status("Resume", "Draft");
} }
hold_purchase_order() { hold_purchase_order() {
@@ -692,15 +692,15 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
} }
unclose_purchase_order() { unclose_purchase_order() {
cur_frm.cscript.update_status("Re-open", "Submitted"); this.frm.cscript.update_status("Re-open", "Submitted");
} }
close_purchase_order() { close_purchase_order() {
cur_frm.cscript.update_status("Close", "Closed"); this.frm.cscript.update_status("Close", "Closed");
} }
delivered_by_supplier() { delivered_by_supplier() {
cur_frm.cscript.update_status("Deliver", "Delivered"); this.frm.cscript.update_status("Deliver", "Delivered");
} }
items_on_form_rendered() { items_on_form_rendered() {

View File

@@ -355,7 +355,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Supplier Address Details", "label": "Supplier Address Details",
"read_only": 1 "read_only": 1
}, },
@@ -394,7 +394,7 @@
}, },
{ {
"fieldname": "shipping_address_display", "fieldname": "shipping_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Shipping Address Details", "label": "Shipping Address Details",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
@@ -1098,7 +1098,7 @@
}, },
{ {
"fieldname": "billing_address_display", "fieldname": "billing_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Billing Address Details", "label": "Billing Address Details",
"read_only": 1 "read_only": 1
}, },
@@ -1288,7 +1288,7 @@
"idx": 105, "idx": 105,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-03-20 16:03:31.611808", "modified": "2024-03-22 16:15:09.674963",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",

View File

@@ -55,7 +55,7 @@ class PurchaseOrder(BuyingController):
) )
additional_discount_percentage: DF.Float additional_discount_percentage: DF.Float
address_display: DF.SmallText | None address_display: DF.TextEditor | None
advance_paid: DF.Currency advance_paid: DF.Currency
advance_payment_status: DF.Literal["Not Initiated", "Initiated", "Partially Paid", "Fully Paid"] advance_payment_status: DF.Literal["Not Initiated", "Initiated", "Partially Paid", "Fully Paid"]
amended_from: DF.Link | None amended_from: DF.Link | None
@@ -74,7 +74,7 @@ class PurchaseOrder(BuyingController):
base_total: DF.Currency base_total: DF.Currency
base_total_taxes_and_charges: DF.Currency base_total_taxes_and_charges: DF.Currency
billing_address: DF.Link | None billing_address: DF.Link | None
billing_address_display: DF.SmallText | None billing_address_display: DF.TextEditor | None
buying_price_list: DF.Link | None buying_price_list: DF.Link | None
company: DF.Link company: DF.Link
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
@@ -131,7 +131,7 @@ class PurchaseOrder(BuyingController):
set_reserve_warehouse: DF.Link | None set_reserve_warehouse: DF.Link | None
set_warehouse: DF.Link | None set_warehouse: DF.Link | None
shipping_address: DF.Link | None shipping_address: DF.Link | None
shipping_address_display: DF.SmallText | None shipping_address_display: DF.TextEditor | None
shipping_rule: DF.Link | None shipping_rule: DF.Link | None
status: DF.Literal[ status: DF.Literal[
"", "",

View File

@@ -303,7 +303,7 @@
}, },
{ {
"fieldname": "billing_address_display", "fieldname": "billing_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Billing Address Details", "label": "Billing Address Details",
"read_only": 1 "read_only": 1
} }
@@ -312,7 +312,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2023-11-06 12:45:28.898706", "modified": "2024-03-22 16:01:19.097788",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Request for Quotation", "name": "Request for Quotation",

View File

@@ -40,7 +40,7 @@ class RequestforQuotation(BuyingController):
amended_from: DF.Link | None amended_from: DF.Link | None
billing_address: DF.Link | None billing_address: DF.Link | None
billing_address_display: DF.SmallText | None billing_address_display: DF.TextEditor | None
company: DF.Link company: DF.Link
email_template: DF.Link | None email_template: DF.Link | None
incoterm: DF.Link | None incoterm: DF.Link | None

View File

@@ -22,9 +22,9 @@ erpnext.buying.SupplierQuotationController = class SupplierQuotationController e
this.frm.set_value("valid_till", frappe.datetime.add_months(this.frm.doc.transaction_date, 1)); this.frm.set_value("valid_till", frappe.datetime.add_months(this.frm.doc.transaction_date, 1));
} }
if (this.frm.doc.docstatus === 1) { if (this.frm.doc.docstatus === 1) {
cur_frm.add_custom_button(__("Purchase Order"), this.make_purchase_order, __("Create")); this.frm.add_custom_button(__("Purchase Order"), this.make_purchase_order, __("Create"));
cur_frm.page.set_inner_btn_group_as_primary(__("Create")); this.frm.page.set_inner_btn_group_as_primary(__("Create"));
cur_frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create")); this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create"));
} else if (this.frm.doc.docstatus === 0) { } else if (this.frm.doc.docstatus === 0) {
this.frm.add_custom_button( this.frm.add_custom_button(
__("Material Request"), __("Material Request"),
@@ -87,13 +87,13 @@ erpnext.buying.SupplierQuotationController = class SupplierQuotationController e
make_purchase_order() { make_purchase_order() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_purchase_order", method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_purchase_order",
frm: cur_frm, frm: this.frm,
}); });
} }
make_quotation() { make_quotation() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_quotation", method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_quotation",
frm: cur_frm, frm: this.frm,
}); });
} }
}; };

View File

@@ -228,7 +228,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Address", "label": "Address",
"read_only": 1 "read_only": 1
}, },
@@ -865,7 +865,7 @@
}, },
{ {
"fieldname": "shipping_address_display", "fieldname": "shipping_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Shipping Address Details", "label": "Shipping Address Details",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
@@ -897,7 +897,7 @@
}, },
{ {
"fieldname": "billing_address_display", "fieldname": "billing_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Billing Address Details", "label": "Billing Address Details",
"read_only": 1 "read_only": 1
}, },
@@ -928,7 +928,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-03-20 16:03:59.069145", "modified": "2024-03-22 16:15:10.122197",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier Quotation", "name": "Supplier Quotation",

View File

@@ -31,7 +31,7 @@ class SupplierQuotation(BuyingController):
) )
additional_discount_percentage: DF.Float additional_discount_percentage: DF.Float
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
apply_discount_on: DF.Literal["", "Grand Total", "Net Total"] apply_discount_on: DF.Literal["", "Grand Total", "Net Total"]
auto_repeat: DF.Link | None auto_repeat: DF.Link | None
@@ -46,7 +46,7 @@ class SupplierQuotation(BuyingController):
base_total: DF.Currency base_total: DF.Currency
base_total_taxes_and_charges: DF.Currency base_total_taxes_and_charges: DF.Currency
billing_address: DF.Link | None billing_address: DF.Link | None
billing_address_display: DF.SmallText | None billing_address_display: DF.TextEditor | None
buying_price_list: DF.Link | None buying_price_list: DF.Link | None
company: DF.Link company: DF.Link
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
@@ -81,7 +81,7 @@ class SupplierQuotation(BuyingController):
rounding_adjustment: DF.Currency rounding_adjustment: DF.Currency
select_print_heading: DF.Link | None select_print_heading: DF.Link | None
shipping_address: DF.Link | None shipping_address: DF.Link | None
shipping_address_display: DF.SmallText | None shipping_address_display: DF.TextEditor | None
shipping_rule: DF.Link | None shipping_rule: DF.Link | None
status: DF.Literal["", "Draft", "Submitted", "Stopped", "Cancelled", "Expired"] status: DF.Literal["", "Draft", "Submitted", "Stopped", "Cancelled", "Expired"]
supplier: DF.Link supplier: DF.Link

View File

@@ -380,6 +380,12 @@ class AccountsController(TransactionBase):
for bundle in bundles: for bundle in bundles:
frappe.delete_doc("Serial and Batch Bundle", bundle.name) frappe.delete_doc("Serial and Batch Bundle", bundle.name)
batches = frappe.get_all(
"Batch", filters={"reference_doctype": self.doctype, "reference_name": self.name}
)
for row in batches:
frappe.delete_doc("Batch", row.name)
def validate_return_against_account(self): def validate_return_against_account(self):
if ( if (
self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against

View File

@@ -48,7 +48,9 @@ class StockController(AccountsController):
super(StockController, self).validate() super(StockController, self).validate()
if self.docstatus == 0: if self.docstatus == 0:
self.validate_duplicate_serial_and_batch_bundle() for table_name in ["items", "packed_items", "supplied_items"]:
self.validate_duplicate_serial_and_batch_bundle(table_name)
if not self.get("is_return"): if not self.get("is_return"):
self.validate_inspection() self.validate_inspection()
self.validate_serialized_batch() self.validate_serialized_batch()
@@ -58,12 +60,19 @@ class StockController(AccountsController):
self.validate_internal_transfer() self.validate_internal_transfer()
self.validate_putaway_capacity() self.validate_putaway_capacity()
def validate_duplicate_serial_and_batch_bundle(self): def validate_duplicate_serial_and_batch_bundle(self, table_name):
if sbb_list := [ if not self.get(table_name):
item.get("serial_and_batch_bundle") return
for item in self.items
if item.get("serial_and_batch_bundle") sbb_list = []
]: for item in self.get(table_name):
if item.get("serial_and_batch_bundle"):
sbb_list.append(item.get("serial_and_batch_bundle"))
if item.get("rejected_serial_and_batch_bundle"):
sbb_list.append(item.get("rejected_serial_and_batch_bundle"))
if sbb_list:
SLE = frappe.qb.DocType("Stock Ledger Entry") SLE = frappe.qb.DocType("Stock Ledger Entry")
data = ( data = (
frappe.qb.from_(SLE) frappe.qb.from_(SLE)
@@ -188,7 +197,7 @@ class StockController(AccountsController):
not row.serial_and_batch_bundle and not row.get("rejected_serial_and_batch_bundle") not row.serial_and_batch_bundle and not row.get("rejected_serial_and_batch_bundle")
): ):
bundle_details = { bundle_details = {
"item_code": row.item_code, "item_code": row.get("rm_item_code") or row.item_code,
"posting_date": self.posting_date, "posting_date": self.posting_date,
"posting_time": self.posting_time, "posting_time": self.posting_time,
"voucher_type": self.doctype, "voucher_type": self.doctype,
@@ -200,7 +209,7 @@ class StockController(AccountsController):
"do_not_submit": True, "do_not_submit": True,
} }
if row.qty: if row.get("qty") or row.get("consumed_qty"):
self.update_bundle_details(bundle_details, table_name, row) self.update_bundle_details(bundle_details, table_name, row)
self.create_serial_batch_bundle(bundle_details, row) self.create_serial_batch_bundle(bundle_details, row)
@@ -219,6 +228,12 @@ class StockController(AccountsController):
type_of_transaction = "Inward" type_of_transaction = "Inward"
if not self.is_return: if not self.is_return:
type_of_transaction = "Outward" type_of_transaction = "Outward"
elif table_name == "supplied_items":
qty = row.consumed_qty
warehouse = self.supplier_warehouse
type_of_transaction = "Outward"
if self.is_return:
type_of_transaction = "Inward"
else: else:
type_of_transaction = get_type_of_transaction(self, row) type_of_transaction = get_type_of_transaction(self, row)
@@ -550,13 +565,30 @@ class StockController(AccountsController):
) )
def delete_auto_created_batches(self): def delete_auto_created_batches(self):
for row in self.items: for table_name in ["items", "packed_items", "supplied_items"]:
if row.serial_and_batch_bundle: if not self.get(table_name):
frappe.db.set_value( continue
"Serial and Batch Bundle", row.serial_and_batch_bundle, {"is_cancelled": 1}
)
row.db_set("serial_and_batch_bundle", None) for row in self.get(table_name):
update_values = {}
if row.get("batch_no"):
update_values["batch_no"] = None
if row.serial_and_batch_bundle:
update_values["serial_and_batch_bundle"] = None
frappe.db.set_value(
"Serial and Batch Bundle", row.serial_and_batch_bundle, {"is_cancelled": 1}
)
if update_values:
row.db_set(update_values)
if table_name == "items" and row.get("rejected_serial_and_batch_bundle"):
frappe.db.set_value(
"Serial and Batch Bundle", row.rejected_serial_and_batch_bundle, {"is_cancelled": 1}
)
row.db_set("rejected_serial_and_batch_bundle", None)
def set_serial_and_batch_bundle(self, table_name=None, ignore_validate=False): def set_serial_and_batch_bundle(self, table_name=None, ignore_validate=False):
if not table_name: if not table_name:

View File

@@ -379,10 +379,10 @@ class SubcontractingController(StockController):
if row.serial_no: if row.serial_no:
details.serial_no.extend(get_serial_nos(row.serial_no)) details.serial_no.extend(get_serial_nos(row.serial_no))
if row.batch_no: elif row.batch_no:
details.batch_no[row.batch_no] += row.qty details.batch_no[row.batch_no] += row.qty
if voucher_bundle_data: elif voucher_bundle_data:
bundle_key = (row.rm_item_code, row.main_item_code, row.t_warehouse, row.voucher_no) bundle_key = (row.rm_item_code, row.main_item_code, row.t_warehouse, row.voucher_no)
bundle_data = voucher_bundle_data.get(bundle_key, frappe._dict()) bundle_data = voucher_bundle_data.get(bundle_key, frappe._dict())
@@ -392,6 +392,9 @@ class SubcontractingController(StockController):
if bundle_data.batch_nos: if bundle_data.batch_nos:
for batch_no, qty in bundle_data.batch_nos.items(): for batch_no, qty in bundle_data.batch_nos.items():
if qty < 0:
qty = abs(qty)
if qty > 0: if qty > 0:
details.batch_no[batch_no] += qty details.batch_no[batch_no] += qty
bundle_data.batch_nos[batch_no] -= qty bundle_data.batch_nos[batch_no] -= qty
@@ -545,17 +548,24 @@ class SubcontractingController(StockController):
rm_obj.reference_name = item_row.name rm_obj.reference_name = item_row.name
use_serial_batch_fields = frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields")
if self.doctype == self.subcontract_data.order_doctype: if self.doctype == self.subcontract_data.order_doctype:
rm_obj.required_qty = qty rm_obj.required_qty = qty
rm_obj.amount = rm_obj.required_qty * rm_obj.rate rm_obj.amount = rm_obj.required_qty * rm_obj.rate
else: else:
rm_obj.consumed_qty = qty rm_obj.consumed_qty = qty
rm_obj.required_qty = bom_item.required_qty or qty rm_obj.required_qty = bom_item.required_qty or qty
rm_obj.serial_and_batch_bundle = None
setattr( setattr(
rm_obj, self.subcontract_data.order_field, item_row.get(self.subcontract_data.order_field) rm_obj, self.subcontract_data.order_field, item_row.get(self.subcontract_data.order_field)
) )
if self.doctype == "Subcontracting Receipt": if use_serial_batch_fields:
rm_obj.use_serial_batch_fields = 1
self.__set_batch_nos(bom_item, item_row, rm_obj, qty)
if self.doctype == "Subcontracting Receipt" and not use_serial_batch_fields:
args = frappe._dict( args = frappe._dict(
{ {
"item_code": rm_obj.rm_item_code, "item_code": rm_obj.rm_item_code,
@@ -581,6 +591,68 @@ class SubcontractingController(StockController):
rm_obj.rate = get_incoming_rate(args) rm_obj.rate = get_incoming_rate(args)
def __set_batch_nos(self, bom_item, item_row, rm_obj, qty):
key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field))
if self.available_materials.get(key) and self.available_materials[key]["batch_no"]:
new_rm_obj = None
for batch_no, batch_qty in self.available_materials[key]["batch_no"].items():
if batch_qty >= qty or (
rm_obj.consumed_qty == 0
and self.backflush_based_on == "BOM"
and len(self.available_materials[key]["batch_no"]) == 1
):
if rm_obj.consumed_qty == 0:
self.__set_consumed_qty(rm_obj, qty)
self.__set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty)
self.available_materials[key]["batch_no"][batch_no] -= qty
return
elif qty > 0 and batch_qty > 0:
qty -= batch_qty
new_rm_obj = self.append(self.raw_material_table, bom_item)
new_rm_obj.serial_and_batch_bundle = None
new_rm_obj.use_serial_batch_fields = 1
new_rm_obj.reference_name = item_row.name
self.__set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty)
self.available_materials[key]["batch_no"][batch_no] = 0
if new_rm_obj:
self.remove(rm_obj)
elif abs(qty) > 0:
self.__set_consumed_qty(rm_obj, qty)
else:
self.__set_consumed_qty(rm_obj, qty, bom_item.required_qty or qty)
self.__set_serial_nos(item_row, rm_obj)
def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0):
rm_obj.required_qty = required_qty
rm_obj.consumed_qty = consumed_qty
def __set_serial_nos(self, item_row, rm_obj):
key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field))
if self.available_materials.get(key) and self.available_materials[key]["serial_no"]:
used_serial_nos = self.available_materials[key]["serial_no"][0 : cint(rm_obj.consumed_qty)]
rm_obj.serial_no = "\n".join(used_serial_nos)
# Removed the used serial nos from the list
for sn in used_serial_nos:
self.available_materials[key]["serial_no"].remove(sn)
def __set_batch_no_as_per_qty(self, item_row, rm_obj, batch_no, qty):
rm_obj.update(
{
"consumed_qty": qty,
"batch_no": batch_no,
"required_qty": qty,
self.subcontract_data.order_field: item_row.get(self.subcontract_data.order_field),
}
)
self.__set_serial_nos(item_row, rm_obj)
def __get_qty_based_on_material_transfer(self, item_row, transfer_item): def __get_qty_based_on_material_transfer(self, item_row, transfer_item):
key = (item_row.item_code, item_row.get(self.subcontract_data.order_field)) key = (item_row.item_code, item_row.get(self.subcontract_data.order_field))
@@ -1076,6 +1148,9 @@ def make_rm_stock_entry(
"serial_and_batch_bundle": rm_item.get("serial_and_batch_bundle"), "serial_and_batch_bundle": rm_item.get("serial_and_batch_bundle"),
"main_item_code": fg_item_code, "main_item_code": fg_item_code,
"allow_alternative_item": item_wh.get(rm_item_code, {}).get("allow_alternative_item"), "allow_alternative_item": item_wh.get(rm_item_code, {}).get("allow_alternative_item"),
"use_serial_batch_fields": rm_item.get("use_serial_batch_fields"),
"serial_no": rm_item.get("serial_no") if rm_item.get("use_serial_batch_fields") else None,
"batch_no": rm_item.get("batch_no") if rm_item.get("use_serial_batch_fields") else None,
} }
} }

View File

@@ -140,6 +140,7 @@ class TestSubcontractingController(FrappeTestCase):
- Create partial SCR against the SCO and check serial nos and batch no. - Create partial SCR against the SCO and check serial nos and batch no.
""" """
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
set_backflush_based_on("Material Transferred for Subcontract") set_backflush_based_on("Material Transferred for Subcontract")
service_items = [ service_items = [
{ {
@@ -202,6 +203,8 @@ class TestSubcontractingController(FrappeTestCase):
if value.get(field): if value.get(field):
self.assertEqual(value.get(field), transferred_detais.get(field)) self.assertEqual(value.get(field), transferred_detais.get(field))
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
def test_subcontracting_with_same_components_different_fg(self): def test_subcontracting_with_same_components_different_fg(self):
""" """
- Set backflush based on Material Transfer. - Set backflush based on Material Transfer.
@@ -211,6 +214,7 @@ class TestSubcontractingController(FrappeTestCase):
- Create partial SCR against the SCO and check serial nos. - Create partial SCR against the SCO and check serial nos.
""" """
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
set_backflush_based_on("Material Transferred for Subcontract") set_backflush_based_on("Material Transferred for Subcontract")
service_items = [ service_items = [
{ {
@@ -278,6 +282,8 @@ class TestSubcontractingController(FrappeTestCase):
self.assertEqual(value.qty, 6) self.assertEqual(value.qty, 6)
self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[6:12])) self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[6:12]))
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
def test_return_non_consumed_materials(self): def test_return_non_consumed_materials(self):
""" """
- Set backflush based on Material Transfer. - Set backflush based on Material Transfer.
@@ -288,6 +294,7 @@ class TestSubcontractingController(FrappeTestCase):
- After that return the non consumed material back to the store from supplier's warehouse. - After that return the non consumed material back to the store from supplier's warehouse.
""" """
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
set_backflush_based_on("Material Transferred for Subcontract") set_backflush_based_on("Material Transferred for Subcontract")
service_items = [ service_items = [
{ {
@@ -333,6 +340,7 @@ class TestSubcontractingController(FrappeTestCase):
get_serial_nos(doc.items[0].serial_no), get_serial_nos(doc.items[0].serial_no),
itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6], itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6],
) )
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
def test_item_with_batch_based_on_bom(self): def test_item_with_batch_based_on_bom(self):
""" """
@@ -578,6 +586,7 @@ class TestSubcontractingController(FrappeTestCase):
- Create SCR for remaining qty against the SCO and change the qty manually. - Create SCR for remaining qty against the SCO and change the qty manually.
""" """
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
set_backflush_based_on("Material Transferred for Subcontract") set_backflush_based_on("Material Transferred for Subcontract")
service_items = [ service_items = [
{ {
@@ -643,6 +652,8 @@ class TestSubcontractingController(FrappeTestCase):
self.assertEqual(value.qty, details.qty) self.assertEqual(value.qty, details.qty)
self.assertEqual(sorted(value.serial_no), sorted(details.serial_no)) self.assertEqual(sorted(value.serial_no), sorted(details.serial_no))
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
def test_incorrect_serial_no_components_based_on_material_transfer(self): def test_incorrect_serial_no_components_based_on_material_transfer(self):
""" """
- Set backflush based on Material Transferred for Subcontract. - Set backflush based on Material Transferred for Subcontract.
@@ -652,6 +663,7 @@ class TestSubcontractingController(FrappeTestCase):
- System should throw the error and not allowed to save the SCR. - System should throw the error and not allowed to save the SCR.
""" """
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
serial_no = "ABC" serial_no = "ABC"
if not frappe.db.exists("Serial No", serial_no): if not frappe.db.exists("Serial No", serial_no):
frappe.get_doc( frappe.get_doc(
@@ -712,6 +724,7 @@ class TestSubcontractingController(FrappeTestCase):
scr1.save() scr1.save()
self.delete_bundle_from_scr(scr1) self.delete_bundle_from_scr(scr1)
scr1.delete() scr1.delete()
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
@staticmethod @staticmethod
def delete_bundle_from_scr(scr): def delete_bundle_from_scr(scr):
@@ -844,6 +857,223 @@ class TestSubcontractingController(FrappeTestCase):
for item in sco.get("supplied_items"): for item in sco.get("supplied_items"):
self.assertEqual(item.supplied_qty, 0.0) self.assertEqual(item.supplied_qty, 0.0)
def test_sco_with_material_transfer_with_use_serial_batch_fields(self):
"""
- Set backflush based on Material Transfer.
- Create SCO for the item Subcontracted Item SA1 and Subcontracted Item SA5.
- Transfer the components from Stores to Supplier warehouse with batch no and serial nos.
- Transfer extra item Subcontracted SRM Item 4 for the subcontract item Subcontracted Item SA5.
- Create partial SCR against the SCO and check serial nos and batch no.
"""
set_backflush_based_on("Material Transferred for Subcontract")
service_items = [
{
"warehouse": "_Test Warehouse - _TC",
"item_code": "Subcontracted Service Item 1",
"qty": 5,
"rate": 100,
"fg_item": "Subcontracted Item SA1",
"fg_item_qty": 5,
},
{
"warehouse": "_Test Warehouse - _TC",
"item_code": "Subcontracted Service Item 5",
"qty": 6,
"rate": 100,
"fg_item": "Subcontracted Item SA5",
"fg_item_qty": 6,
},
]
sco = get_subcontracting_order(service_items=service_items)
rm_items = get_rm_items(sco.supplied_items)
rm_items.append(
{
"main_item_code": "Subcontracted Item SA5",
"item_code": "Subcontracted SRM Item 4",
"qty": 6,
}
)
itemwise_details = make_stock_in_entry(rm_items=rm_items)
for item in rm_items:
item["sco_rm_detail"] = sco.items[0].name if item.get("qty") == 5 else sco.items[1].name
make_stock_transfer_entry(
sco_no=sco.name,
rm_items=rm_items,
itemwise_details=copy.deepcopy(itemwise_details),
)
scr1 = make_subcontracting_receipt(sco.name)
scr1.remove(scr1.items[1])
scr1.save()
scr1.submit()
for key, value in get_supplied_items(scr1).items():
transferred_detais = itemwise_details.get(key)
for field in ["qty", "serial_no", "batch_no"]:
if value.get(field):
data = value.get(field)
if field == "serial_no":
data = sorted(data)
self.assertEqual(data, transferred_detais.get(field))
scr2 = make_subcontracting_receipt(sco.name)
scr2.save()
scr2.submit()
for key, value in get_supplied_items(scr2).items():
transferred_detais = itemwise_details.get(key)
for field in ["qty", "serial_no", "batch_no"]:
if value.get(field):
data = value.get(field)
if field == "serial_no":
data = sorted(data)
self.assertEqual(data, transferred_detais.get(field))
def test_subcontracting_with_same_components_different_fg_with_serial_batch_fields(self):
"""
- Set backflush based on Material Transfer.
- Create SCO for the item Subcontracted Item SA2 and Subcontracted Item SA3.
- Transfer the components from Stores to Supplier warehouse with serial nos.
- Transfer extra qty of components for the item Subcontracted Item SA2.
- Create partial SCR against the SCO and check serial nos.
"""
set_backflush_based_on("Material Transferred for Subcontract")
service_items = [
{
"warehouse": "_Test Warehouse - _TC",
"item_code": "Subcontracted Service Item 2",
"qty": 5,
"rate": 100,
"fg_item": "Subcontracted Item SA2",
"fg_item_qty": 5,
},
{
"warehouse": "_Test Warehouse - _TC",
"item_code": "Subcontracted Service Item 3",
"qty": 6,
"rate": 100,
"fg_item": "Subcontracted Item SA3",
"fg_item_qty": 6,
},
]
sco = get_subcontracting_order(service_items=service_items)
rm_items = get_rm_items(sco.supplied_items)
rm_items[0]["qty"] += 1
itemwise_details = make_stock_in_entry(rm_items=rm_items)
for item in rm_items:
item["sco_rm_detail"] = sco.items[0].name if item.get("qty") == 5 else sco.items[1].name
item["use_serial_batch_fields"] = 1
make_stock_transfer_entry(
sco_no=sco.name,
rm_items=rm_items,
itemwise_details=copy.deepcopy(itemwise_details),
)
scr1 = make_subcontracting_receipt(sco.name)
scr1.items[0].qty = 3
scr1.remove(scr1.items[1])
scr1.save()
scr1.submit()
for key, value in get_supplied_items(scr1).items():
transferred_detais = itemwise_details.get(key)
self.assertEqual(value.qty, 4)
self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:4]))
scr2 = make_subcontracting_receipt(sco.name)
scr2.items[0].qty = 2
scr2.remove(scr2.items[1])
scr2.save()
scr2.submit()
for key, value in get_supplied_items(scr2).items():
transferred_detais = itemwise_details.get(key)
self.assertEqual(value.qty, 2)
self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[4:6]))
scr3 = make_subcontracting_receipt(sco.name)
scr3.save()
scr3.submit()
for key, value in get_supplied_items(scr3).items():
transferred_detais = itemwise_details.get(key)
self.assertEqual(value.qty, 6)
self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[6:12]))
def test_return_non_consumed_materials_with_serial_batch_fields(self):
"""
- Set backflush based on Material Transfer.
- Create SCO for item Subcontracted Item SA2.
- Transfer the components from Stores to Supplier warehouse with serial nos.
- Transfer extra qty of component for the subcontracted item Subcontracted Item SA2.
- Create SCR for full qty against the SCO and change the qty of raw material.
- After that return the non consumed material back to the store from supplier's warehouse.
"""
set_backflush_based_on("Material Transferred for Subcontract")
service_items = [
{
"warehouse": "_Test Warehouse - _TC",
"item_code": "Subcontracted Service Item 2",
"qty": 5,
"rate": 100,
"fg_item": "Subcontracted Item SA2",
"fg_item_qty": 5,
},
]
sco = get_subcontracting_order(service_items=service_items)
rm_items = get_rm_items(sco.supplied_items)
rm_items[0]["qty"] += 1
itemwise_details = make_stock_in_entry(rm_items=rm_items)
for item in rm_items:
item["use_serial_batch_fields"] = 1
item["sco_rm_detail"] = sco.items[0].name
make_stock_transfer_entry(
sco_no=sco.name,
rm_items=rm_items,
itemwise_details=copy.deepcopy(itemwise_details),
)
scr1 = make_subcontracting_receipt(sco.name)
scr1.save()
scr1.supplied_items[0].consumed_qty = 5
scr1.supplied_items[0].serial_no = "\n".join(
sorted(itemwise_details.get("Subcontracted SRM Item 2").get("serial_no")[0:5])
)
scr1.submit()
for key, value in get_supplied_items(scr1).items():
transferred_detais = itemwise_details.get(key)
self.assertTrue(value.use_serial_batch_fields)
self.assertEqual(value.qty, 5)
self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:5]))
sco.load_from_db()
self.assertEqual(sco.supplied_items[0].consumed_qty, 5)
doc = get_materials_from_supplier(sco.name, [d.name for d in sco.supplied_items])
self.assertEqual(doc.items[0].qty, 1)
self.assertEqual(doc.items[0].s_warehouse, "_Test Warehouse 1 - _TC")
self.assertEqual(doc.items[0].t_warehouse, "_Test Warehouse - _TC")
self.assertEqual(
get_serial_nos(doc.items[0].serial_no),
itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6],
)
def add_second_row_in_scr(scr): def add_second_row_in_scr(scr):
item_dict = {} item_dict = {}
@@ -914,6 +1144,7 @@ def update_item_details(child_row, details):
else child_row.get("consumed_qty") else child_row.get("consumed_qty")
) )
details.use_serial_batch_fields = child_row.get("use_serial_batch_fields")
if child_row.serial_and_batch_bundle: if child_row.serial_and_batch_bundle:
doc = frappe.get_doc("Serial and Batch Bundle", child_row.serial_and_batch_bundle) doc = frappe.get_doc("Serial and Batch Bundle", child_row.serial_and_batch_bundle)
for row in doc.get("entries"): for row in doc.get("entries"):
@@ -945,6 +1176,7 @@ def make_stock_transfer_entry(**args):
"rate": row.rate or 100, "rate": row.rate or 100,
"stock_uom": row.stock_uom or "Nos", "stock_uom": row.stock_uom or "Nos",
"warehouse": row.warehouse or "_Test Warehouse - _TC", "warehouse": row.warehouse or "_Test Warehouse - _TC",
"use_serial_batch_fields": row.get("use_serial_batch_fields"),
} }
item_details = args.itemwise_details.get(row.item_code) item_details = args.itemwise_details.get(row.item_code)
@@ -960,9 +1192,12 @@ def make_stock_transfer_entry(**args):
if batch_qty >= row.qty: if batch_qty >= row.qty:
batches[batch_no] = row.qty batches[batch_no] = row.qty
item_details.batch_no[batch_no] -= row.qty item_details.batch_no[batch_no] -= row.qty
if row.get("use_serial_batch_fields"):
item["batch_no"] = batch_no
break break
if serial_nos or batches: if not row.get("use_serial_batch_fields") and (serial_nos or batches):
item["serial_and_batch_bundle"] = make_serial_batch_bundle( item["serial_and_batch_bundle"] = make_serial_batch_bundle(
frappe._dict( frappe._dict(
{ {
@@ -978,6 +1213,9 @@ def make_stock_transfer_entry(**args):
) )
).name ).name
if serial_nos and row.get("use_serial_batch_fields"):
item["serial_no"] = "\n".join(serial_nos)
items.append(item) items.append(item)
ste_dict = make_rm_stock_entry(args.sco_no, items) ste_dict = make_rm_stock_entry(args.sco_no, items)
@@ -1132,6 +1370,7 @@ def get_rm_items(supplied_items):
"rate": item.rate, "rate": item.rate,
"stock_uom": item.stock_uom, "stock_uom": item.stock_uom,
"warehouse": item.reserve_warehouse, "warehouse": item.reserve_warehouse,
"use_serial_batch_fields": 0,
} }
) )

View File

@@ -11,7 +11,7 @@ frappe.ui.form.on("Campaign", {
frappe.boot.sysdefaults.campaign_naming_by == "Naming Series" frappe.boot.sysdefaults.campaign_naming_by == "Naming Series"
); );
} else { } else {
cur_frm.add_custom_button( frm.add_custom_button(
__("View Leads"), __("View Leads"),
function () { function () {
frappe.route_options = { source: "Campaign", campaign_name: frm.doc.name }; frappe.route_options = { source: "Campaign", campaign_name: frm.doc.name };

View File

@@ -89,32 +89,33 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller
make_customer() { make_customer() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_customer", method: "erpnext.crm.doctype.lead.lead.make_customer",
frm: cur_frm, frm: this.frm,
}); });
} }
make_quotation() { make_quotation() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_quotation", method: "erpnext.crm.doctype.lead.lead.make_quotation",
frm: cur_frm, frm: this.frm,
}); });
} }
make_prospect() { make_prospect() {
const me = this;
frappe.model.with_doctype("Prospect", function () { frappe.model.with_doctype("Prospect", function () {
let prospect = frappe.model.get_new_doc("Prospect"); let prospect = frappe.model.get_new_doc("Prospect");
prospect.company_name = cur_frm.doc.company_name; prospect.company_name = me.frm.doc.company_name;
prospect.no_of_employees = cur_frm.doc.no_of_employees; prospect.no_of_employees = me.frm.doc.no_of_employees;
prospect.industry = cur_frm.doc.industry; prospect.industry = me.frm.doc.industry;
prospect.market_segment = cur_frm.doc.market_segment; prospect.market_segment = me.frm.doc.market_segment;
prospect.territory = cur_frm.doc.territory; prospect.territory = me.frm.doc.territory;
prospect.fax = cur_frm.doc.fax; prospect.fax = me.frm.doc.fax;
prospect.website = cur_frm.doc.website; prospect.website = me.frm.doc.website;
prospect.prospect_owner = cur_frm.doc.lead_owner; prospect.prospect_owner = me.frm.doc.lead_owner;
prospect.notes = cur_frm.doc.notes; prospect.notes = me.frm.doc.notes;
let leads_row = frappe.model.add_child(prospect, "leads"); let leads_row = frappe.model.add_child(prospect, "leads");
leads_row.lead = cur_frm.doc.name; leads_row.lead = me.frm.doc.name;
frappe.set_route("Form", "Prospect", prospect.name); frappe.set_route("Form", "Prospect", prospect.name);
}); });

View File

@@ -318,14 +318,14 @@ erpnext.crm.Opportunity = class Opportunity extends frappe.ui.form.Controller {
create_quotation() { create_quotation() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation", method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation",
frm: cur_frm, frm: this.frm,
}); });
} }
make_customer() { make_customer() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.opportunity.opportunity.make_customer", method: "erpnext.crm.doctype.opportunity.opportunity.make_customer",
frm: cur_frm, frm: this.frm,
}); });
} }

View File

@@ -250,7 +250,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hidden": 1, "hidden": 1,
"label": "Address", "label": "Address",
"oldfieldname": "address", "oldfieldname": "address",
@@ -622,7 +622,7 @@
"icon": "fa fa-info-sign", "icon": "fa fa-info-sign",
"idx": 195, "idx": 195,
"links": [], "links": [],
"modified": "2022-10-13 12:42:21.545636", "modified": "2024-03-22 16:01:10.721453",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Opportunity", "name": "Opportunity",

View File

@@ -40,7 +40,7 @@ class Opportunity(TransactionBase, CRMNote):
OpportunityLostReasonDetail, OpportunityLostReasonDetail,
) )
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
annual_revenue: DF.Currency annual_revenue: DF.Currency
base_opportunity_amount: DF.Currency base_opportunity_amount: DF.Currency

View File

@@ -187,7 +187,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hidden": 1, "hidden": 1,
"label": "Address", "label": "Address",
"read_only": 1 "read_only": 1
@@ -238,7 +238,7 @@
"link_fieldname": "maintenance_schedule" "link_fieldname": "maintenance_schedule"
} }
], ],
"modified": "2023-06-03 16:15:43.958072", "modified": "2024-03-22 16:01:11.868813",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Maintenance", "module": "Maintenance",
"name": "Maintenance Schedule", "name": "Maintenance Schedule",

View File

@@ -26,7 +26,7 @@ class MaintenanceSchedule(TransactionBase):
MaintenanceScheduleItem, MaintenanceScheduleItem,
) )
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
company: DF.Link company: DF.Link
contact_display: DF.SmallText | None contact_display: DF.SmallText | None

View File

@@ -83,7 +83,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hidden": 1, "hidden": 1,
"label": "Address", "label": "Address",
"read_only": 1 "read_only": 1
@@ -295,7 +295,7 @@
"idx": 1, "idx": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2023-06-03 16:19:07.902723", "modified": "2024-03-22 16:01:12.354826",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Maintenance", "module": "Maintenance",
"name": "Maintenance Visit", "name": "Maintenance Visit",

View File

@@ -22,7 +22,7 @@ class MaintenanceVisit(TransactionBase):
MaintenanceVisitPurpose, MaintenanceVisitPurpose,
) )
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
company: DF.Link company: DF.Link
completion_status: DF.Literal["", "Partially Completed", "Fully Completed"] completion_status: DF.Literal["", "Partially Completed", "Fully Completed"]

View File

@@ -1208,6 +1208,51 @@ class TestWorkOrder(FrappeTestCase):
except frappe.MandatoryError: except frappe.MandatoryError:
self.fail("Batch generation causing failing in Work Order") self.fail("Batch generation causing failing in Work Order")
@change_settings("Manufacturing Settings", {"make_serial_no_batch_from_work_order": 1})
def test_auto_serial_no_batch_creation(self):
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
fg_item = frappe.generate_hash(length=20)
child_item = frappe.generate_hash(length=20)
bom_tree = {fg_item: {child_item: {}}}
create_nested_bom(bom_tree, prefix="")
item = frappe.get_doc("Item", fg_item)
item.update(
{
"has_serial_no": 1,
"has_batch_no": 1,
"serial_no_series": f"SN-TEST-{item.name}.#####",
"create_new_batch": 1,
"batch_number_series": f"BATCH-TEST-{item.name}.#####",
}
)
item.save()
try:
wo_order = make_wo_order_test_record(item=fg_item, batch_size=5, qty=10, skip_transfer=True)
serial_nos = self.get_serial_nos_for_fg(wo_order.name)
stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
stock_entry.set_work_order_details()
stock_entry.set_serial_no_batch_for_finished_good()
for row in stock_entry.items:
if row.item_code == fg_item:
self.assertTrue(row.serial_and_batch_bundle)
self.assertEqual(
sorted(get_serial_nos_from_bundle(row.serial_and_batch_bundle)), sorted(serial_nos)
)
sn_doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle)
for row in sn_doc.entries:
self.assertTrue(row.serial_no)
self.assertTrue(row.batch_no)
except frappe.MandatoryError:
self.fail("Batch generation causing failing in Work Order")
def get_serial_nos_for_fg(self, work_order): def get_serial_nos_for_fg(self, work_order):
serial_nos = [] serial_nos = []
for row in frappe.get_all("Serial No", filters={"work_order": work_order}): for row in frappe.get_all("Serial No", filters={"work_order": work_order}):
@@ -2270,6 +2315,7 @@ def make_wo_order_test_record(**args):
wo_order.planned_start_date = args.planned_start_date or now() wo_order.planned_start_date = args.planned_start_date or now()
wo_order.transfer_material_against = args.transfer_material_against or "Work Order" wo_order.transfer_material_against = args.transfer_material_against or "Work Order"
wo_order.from_wip_warehouse = args.from_wip_warehouse or 0 wo_order.from_wip_warehouse = args.from_wip_warehouse or 0
wo_order.batch_size = args.batch_size or 0
if args.source_warehouse: if args.source_warehouse:
for item in wo_order.get("required_items"): for item in wo_order.get("required_items"):

View File

@@ -536,6 +536,12 @@ class WorkOrder(Document):
"Item", self.production_item, ["serial_no_series", "item_name", "description"], as_dict=1 "Item", self.production_item, ["serial_no_series", "item_name", "description"], as_dict=1
) )
batches = []
if self.has_batch_no:
batches = frappe.get_all(
"Batch", filters={"reference_name": self.name}, order_by="creation", pluck="name"
)
serial_nos = [] serial_nos = []
if item_details.serial_no_series: if item_details.serial_no_series:
serial_nos = get_available_serial_nos(item_details.serial_no_series, self.qty) serial_nos = get_available_serial_nos(item_details.serial_no_series, self.qty)
@@ -556,10 +562,20 @@ class WorkOrder(Document):
"description", "description",
"status", "status",
"work_order", "work_order",
"batch_no",
] ]
serial_nos_details = [] serial_nos_details = []
index = 0
for serial_no in serial_nos: for serial_no in serial_nos:
index += 1
batch_no = None
if batches and self.batch_size:
batch_no = batches[0]
if index % self.batch_size == 0:
batches.remove(batch_no)
serial_nos_details.append( serial_nos_details.append(
( (
serial_no, serial_no,
@@ -574,6 +590,7 @@ class WorkOrder(Document):
item_details.description, item_details.description,
"Inactive", "Inactive",
self.name, self.name,
batch_no,
) )
) )

View File

@@ -356,7 +356,7 @@ erpnext.patches.v14_0.update_total_asset_cost_field
erpnext.patches.v15_0.create_advance_payment_status erpnext.patches.v15_0.create_advance_payment_status
erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes
erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool
erpnext.patches.v14_0.update_flag_for_return_invoices erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22
# below migration patch should always run last # below migration patch should always run last
erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.patches.v14_0.migrate_gl_to_payment_ledger
erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20

View File

@@ -12,6 +12,10 @@ def execute():
creation_date = "2024-01-25" creation_date = "2024-01-25"
si = qb.DocType("Sales Invoice") si = qb.DocType("Sales Invoice")
# unset flag, as migration would have set it for all records, as the field was introduced with default '1'
qb.update(si).set(si.update_outstanding_for_self, False).run()
if cr_notes := ( if cr_notes := (
qb.from_(si) qb.from_(si)
.select(si.name) .select(si.name)
@@ -37,6 +41,10 @@ def execute():
).run() ).run()
pi = qb.DocType("Purchase Invoice") pi = qb.DocType("Purchase Invoice")
# unset flag, as migration would have set it for all records, as the field was introduced with default '1'
qb.update(pi).set(pi.update_outstanding_for_self, False).run()
if dr_notes := ( if dr_notes := (
qb.from_(pi) qb.from_(pi)
.select(pi.name) .select(pi.name)

View File

@@ -2267,9 +2267,9 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
} }
get_method_for_payment() { get_method_for_payment() {
var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"; let method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
if(cur_frm.doc.__onload && cur_frm.doc.__onload.make_payment_via_journal_entry){ if(this.frm.doc.__onload && this.frm.doc.__onload.make_payment_via_journal_entry){
if(['Sales Invoice', 'Purchase Invoice'].includes( cur_frm.doc.doctype)){ if(['Sales Invoice', 'Purchase Invoice'].includes( this.frm.doc.doctype)){
method = "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice"; method = "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice";
}else { }else {
method= "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order"; method= "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order";

View File

@@ -110,7 +110,7 @@ erpnext.timesheet.control_timer = function (frm, dialog, row, timestamp = 0) {
// Stop the timer and update the time logged by the timer on click of 'Complete' button // Stop the timer and update the time logged by the timer on click of 'Complete' button
$btn_complete.click(function () { $btn_complete.click(function () {
var grid_row = cur_frm.fields_dict["time_logs"].grid.get_row(row.idx - 1); var grid_row = frm.fields_dict["time_logs"].grid.get_row(row.idx - 1);
var args = dialog.get_values(); var args = dialog.get_values();
grid_row.doc.completed = 1; grid_row.doc.completed = 1;
grid_row.doc.activity_type = args.activity_type; grid_row.doc.activity_type = args.activity_type;

View File

@@ -549,6 +549,14 @@ body[data-route="pos"] {
justify-content: center; justify-content: center;
} }
.frappe-control[data-fieldname="other_charges_calculation"] .ql-editor { .frappe-control[data-fieldname="other_charges_calculation"] .ql-editor,
.frappe-control[data-fieldname="address_display"] .ql-editor,
.frappe-control[data-fieldname="shipping_address_display"] .ql-editor,
.frappe-control[data-fieldname="shipping_address"] .ql-editor,
.frappe-control[data-fieldname="dispatch_address"] .ql-editor,
.frappe-control[data-fieldname="source_address_display"] .ql-editor,
.frappe-control[data-fieldname="target_address_display"] .ql-editor,
.frappe-control[data-fieldname="billing_address_display"] .ql-editor,
.frappe-control[data-fieldname="company_address_display"] .ql-editor {
white-space: normal; white-space: normal;
} }

View File

@@ -94,7 +94,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hidden": 1, "hidden": 1,
"label": "Address", "label": "Address",
"read_only": 1 "read_only": 1
@@ -238,7 +238,7 @@
"idx": 1, "idx": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-02-04 18:20:12.020313", "modified": "2024-03-22 16:01:13.513355",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Installation Note", "name": "Installation Note",

View File

@@ -23,7 +23,7 @@ class InstallationNote(TransactionBase):
InstallationNoteItem, InstallationNoteItem,
) )
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
company: DF.Link company: DF.Link
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
@@ -38,6 +38,7 @@ class InstallationNote(TransactionBase):
inst_time: DF.Time | None inst_time: DF.Time | None
items: DF.Table[InstallationNoteItem] items: DF.Table[InstallationNoteItem]
naming_series: DF.Literal["MAT-INS-.YYYY.-"] naming_series: DF.Literal["MAT-INS-.YYYY.-"]
project: DF.Link | None
remarks: DF.SmallText | None remarks: DF.SmallText | None
status: DF.Literal["Draft", "Submitted", "Cancelled"] status: DF.Literal["Draft", "Submitted", "Cancelled"]
territory: DF.Link territory: DF.Link

View File

@@ -265,7 +265,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Address", "label": "Address",
"oldfieldname": "customer_address", "oldfieldname": "customer_address",
"oldfieldtype": "Small Text", "oldfieldtype": "Small Text",
@@ -318,7 +318,7 @@
}, },
{ {
"fieldname": "shipping_address", "fieldname": "shipping_address",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Shipping Address", "label": "Shipping Address",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
@@ -965,7 +965,7 @@
}, },
{ {
"fieldname": "company_address_display", "fieldname": "company_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Company Address", "label": "Company Address",
"read_only": 1 "read_only": 1
}, },
@@ -1073,7 +1073,7 @@
"idx": 82, "idx": 82,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-03-20 16:04:21.567847", "modified": "2024-03-22 16:15:10.488656",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Quotation", "name": "Quotation",

View File

@@ -34,7 +34,7 @@ class Quotation(SellingController):
from erpnext.stock.doctype.packed_item.packed_item import PackedItem from erpnext.stock.doctype.packed_item.packed_item import PackedItem
additional_discount_percentage: DF.Float additional_discount_percentage: DF.Float
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
apply_discount_on: DF.Literal["", "Grand Total", "Net Total"] apply_discount_on: DF.Literal["", "Grand Total", "Net Total"]
auto_repeat: DF.Link | None auto_repeat: DF.Link | None
@@ -49,7 +49,7 @@ class Quotation(SellingController):
campaign: DF.Link | None campaign: DF.Link | None
company: DF.Link company: DF.Link
company_address: DF.Link | None company_address: DF.Link | None
company_address_display: DF.SmallText | None company_address_display: DF.TextEditor | None
competitors: DF.TableMultiSelect[CompetitorDetail] competitors: DF.TableMultiSelect[CompetitorDetail]
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
contact_email: DF.Data | None contact_email: DF.Data | None
@@ -93,7 +93,7 @@ class Quotation(SellingController):
scan_barcode: DF.Data | None scan_barcode: DF.Data | None
select_print_heading: DF.Link | None select_print_heading: DF.Link | None
selling_price_list: DF.Link selling_price_list: DF.Link
shipping_address: DF.SmallText | None shipping_address: DF.TextEditor | None
shipping_address_name: DF.Link | None shipping_address_name: DF.Link | None
shipping_rule: DF.Link | None shipping_rule: DF.Link | None
source: DF.Link | None source: DF.Link | None

View File

@@ -369,7 +369,7 @@
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Address", "label": "Address",
@@ -415,7 +415,7 @@
}, },
{ {
"fieldname": "company_address_display", "fieldname": "company_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Company Address", "label": "Company Address",
@@ -450,7 +450,7 @@
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"fieldname": "shipping_address", "fieldname": "shipping_address",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Shipping Address", "label": "Shipping Address",
@@ -1525,7 +1525,7 @@
"allow_on_submit": 1, "allow_on_submit": 1,
"depends_on": "dispatch_address_name", "depends_on": "dispatch_address_name",
"fieldname": "dispatch_address", "fieldname": "dispatch_address",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Dispatch Address", "label": "Dispatch Address",
"read_only": 1 "read_only": 1
}, },
@@ -1657,7 +1657,7 @@
"idx": 105, "idx": 105,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-03-20 16:04:43.627183", "modified": "2024-03-22 16:15:04.884816",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order", "name": "Sales Order",

View File

@@ -64,7 +64,7 @@ class SalesOrder(SellingController):
from erpnext.stock.doctype.packed_item.packed_item import PackedItem from erpnext.stock.doctype.packed_item.packed_item import PackedItem
additional_discount_percentage: DF.Float additional_discount_percentage: DF.Float
address_display: DF.SmallText | None address_display: DF.TextEditor | None
advance_paid: DF.Currency advance_paid: DF.Currency
advance_payment_status: DF.Literal["Not Requested", "Requested", "Partially Paid", "Fully Paid"] advance_payment_status: DF.Literal["Not Requested", "Requested", "Partially Paid", "Fully Paid"]
amended_from: DF.Link | None amended_from: DF.Link | None
@@ -84,7 +84,7 @@ class SalesOrder(SellingController):
commission_rate: DF.Float commission_rate: DF.Float
company: DF.Link company: DF.Link
company_address: DF.Link | None company_address: DF.Link | None
company_address_display: DF.SmallText | None company_address_display: DF.TextEditor | None
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
contact_email: DF.Data | None contact_email: DF.Data | None
contact_mobile: DF.SmallText | None contact_mobile: DF.SmallText | None
@@ -104,7 +104,7 @@ class SalesOrder(SellingController):
] ]
disable_rounded_total: DF.Check disable_rounded_total: DF.Check
discount_amount: DF.Currency discount_amount: DF.Currency
dispatch_address: DF.SmallText | None dispatch_address: DF.TextEditor | None
dispatch_address_name: DF.Link | None dispatch_address_name: DF.Link | None
from_date: DF.Date | None from_date: DF.Date | None
grand_total: DF.Currency grand_total: DF.Currency
@@ -147,7 +147,7 @@ class SalesOrder(SellingController):
select_print_heading: DF.Link | None select_print_heading: DF.Link | None
selling_price_list: DF.Link selling_price_list: DF.Link
set_warehouse: DF.Link | None set_warehouse: DF.Link | None
shipping_address: DF.SmallText | None shipping_address: DF.TextEditor | None
shipping_address_name: DF.Link | None shipping_address_name: DF.Link | None
shipping_rule: DF.Link | None shipping_rule: DF.Link | None
skip_delivery_note: DF.Check skip_delivery_note: DF.Check
@@ -755,6 +755,13 @@ def get_list_context(context=None):
return list_context return list_context
@frappe.whitelist()
def is_enable_cutoff_date_on_bulk_delivery_note_creation():
return frappe.db.get_single_value(
"Selling Settings", "enable_cutoff_date_on_bulk_delivery_note_creation"
)
@frappe.whitelist() @frappe.whitelist()
def close_or_unclose_sales_orders(names, status): def close_or_unclose_sales_orders(names, status):
if not frappe.has_permission("Sales Order", "write"): if not frappe.has_permission("Sales Order", "write"):

View File

@@ -68,10 +68,10 @@ frappe.listview_settings["Sales Order"] = {
}); });
listview.page.add_action_item(__("Delivery Note"), () => { listview.page.add_action_item(__("Delivery Note"), () => {
frappe.db frappe.call({
.get_single_value("Selling Settings", "enable_cutoff_date_on_bulk_delivery_note_creation") method: "erpnext.selling.doctype.sales_order.sales_order.is_enable_cutoff_date_on_bulk_delivery_note_creation",
.then((value) => { callback: (r) => {
if (value) { if (r.message) {
var dialog = new frappe.ui.Dialog({ var dialog = new frappe.ui.Dialog({
title: __("Select Items up to Delivery Date"), title: __("Select Items up to Delivery Date"),
fields: [ fields: [
@@ -98,7 +98,8 @@ frappe.listview_settings["Sales Order"] = {
} else { } else {
erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Delivery Note"); erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Delivery Note");
} }
}); },
});
}); });
listview.page.add_action_item(__("Advance Payment"), () => { listview.page.add_action_item(__("Advance Payment"), () => {

View File

@@ -834,7 +834,8 @@
"label": "Purchase Order", "label": "Purchase Order",
"options": "Purchase Order", "options": "Purchase Order",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1,
"search_index": 1
}, },
{ {
"fieldname": "column_break_89", "fieldname": "column_break_89",
@@ -909,7 +910,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-01-25 14:24:00.330219", "modified": "2024-03-21 18:15:56.625005",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order Item", "name": "Sales Order Item",

View File

@@ -245,6 +245,8 @@ class DeprecatedBatchNoValuation:
if self.sle.serial_and_batch_bundle: if self.sle.serial_and_batch_bundle:
query = query.where(bundle.name != self.sle.serial_and_batch_bundle) query = query.where(bundle.name != self.sle.serial_and_batch_bundle)
query = query.where(bundle.voucher_type != "Pick List")
for d in query.run(as_dict=True): for d in query.run(as_dict=True):
self.non_batchwise_balance_value += flt(d.batch_value) self.non_batchwise_balance_value += flt(d.batch_value)
self.non_batchwise_balance_qty += flt(d.batch_qty) self.non_batchwise_balance_qty += flt(d.batch_qty)

View File

@@ -351,7 +351,7 @@
}, },
{ {
"fieldname": "shipping_address", "fieldname": "shipping_address",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Shipping Address", "label": "Shipping Address",
"read_only": 1 "read_only": 1
}, },
@@ -408,7 +408,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Billing Address", "label": "Billing Address",
"read_only": 1 "read_only": 1
}, },
@@ -420,7 +420,7 @@
}, },
{ {
"fieldname": "company_address_display", "fieldname": "company_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Company Address", "label": "Company Address",
"read_only": 1 "read_only": 1
}, },
@@ -1289,7 +1289,7 @@
{ {
"depends_on": "dispatch_address_name", "depends_on": "dispatch_address_name",
"fieldname": "dispatch_address", "fieldname": "dispatch_address",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Dispatch Address", "label": "Dispatch Address",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
@@ -1397,7 +1397,7 @@
"idx": 146, "idx": 146,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-03-20 16:05:02.854990", "modified": "2024-03-22 16:15:07.253135",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Note", "name": "Delivery Note",

View File

@@ -35,7 +35,7 @@ class DeliveryNote(SellingController):
from erpnext.stock.doctype.packed_item.packed_item import PackedItem from erpnext.stock.doctype.packed_item.packed_item import PackedItem
additional_discount_percentage: DF.Float additional_discount_percentage: DF.Float
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
amount_eligible_for_commission: DF.Currency amount_eligible_for_commission: DF.Currency
apply_discount_on: DF.Literal["", "Grand Total", "Net Total"] apply_discount_on: DF.Literal["", "Grand Total", "Net Total"]
@@ -52,7 +52,7 @@ class DeliveryNote(SellingController):
commission_rate: DF.Float commission_rate: DF.Float
company: DF.Link company: DF.Link
company_address: DF.Link | None company_address: DF.Link | None
company_address_display: DF.SmallText | None company_address_display: DF.TextEditor | None
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
contact_email: DF.Data | None contact_email: DF.Data | None
contact_mobile: DF.SmallText | None contact_mobile: DF.SmallText | None
@@ -66,7 +66,7 @@ class DeliveryNote(SellingController):
customer_name: DF.Data | None customer_name: DF.Data | None
disable_rounded_total: DF.Check disable_rounded_total: DF.Check
discount_amount: DF.Currency discount_amount: DF.Currency
dispatch_address: DF.SmallText | None dispatch_address: DF.TextEditor | None
dispatch_address_name: DF.Link | None dispatch_address_name: DF.Link | None
driver: DF.Link | None driver: DF.Link | None
driver_name: DF.Data | None driver_name: DF.Data | None
@@ -117,7 +117,7 @@ class DeliveryNote(SellingController):
set_posting_time: DF.Check set_posting_time: DF.Check
set_target_warehouse: DF.Link | None set_target_warehouse: DF.Link | None
set_warehouse: DF.Link | None set_warehouse: DF.Link | None
shipping_address: DF.SmallText | None shipping_address: DF.TextEditor | None
shipping_address_name: DF.Link | None shipping_address_name: DF.Link | None
shipping_rule: DF.Link | None shipping_rule: DF.Link | None
source: DF.Link | None source: DF.Link | None

View File

@@ -796,7 +796,8 @@
"label": "Purchase Order", "label": "Purchase Order",
"options": "Purchase Order", "options": "Purchase Order",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1,
"search_index": 1
}, },
{ {
"fieldname": "column_break_82", "fieldname": "column_break_82",
@@ -912,7 +913,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-02-04 14:10:31.750340", "modified": "2024-03-21 18:15:07.603672",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Note Item", "name": "Delivery Note Item",

View File

@@ -7,7 +7,6 @@ from frappe import _, bold
from frappe.model.document import Document from frappe.model.document import Document
from frappe.query_builder import Criterion from frappe.query_builder import Criterion
from frappe.query_builder.functions import Cast_ from frappe.query_builder.functions import Cast_
from frappe.utils import getdate
class ItemPriceDuplicateItem(frappe.ValidationError): class ItemPriceDuplicateItem(frappe.ValidationError):
@@ -46,7 +45,7 @@ class ItemPrice(Document):
def validate(self): def validate(self):
self.validate_item() self.validate_item()
self.validate_dates() self.validate_from_to_dates("valid_from", "valid_upto")
self.update_price_list_details() self.update_price_list_details()
self.update_item_details() self.update_item_details()
self.check_duplicates() self.check_duplicates()
@@ -61,11 +60,6 @@ class ItemPrice(Document):
): ):
frappe.throw(_("UOM {0} not found in Item {1}").format(self.uom, self.item_code)) frappe.throw(_("UOM {0} not found in Item {1}").format(self.uom, self.item_code))
def validate_dates(self):
if self.valid_from and self.valid_upto:
if getdate(self.valid_from) > getdate(self.valid_upto):
frappe.throw(_("Valid From Date must be lesser than Valid Up To Date."))
def update_price_list_details(self): def update_price_list_details(self):
if self.price_list: if self.price_list:
price_list_details = frappe.db.get_value( price_list_details = frappe.db.get_value(

View File

@@ -313,7 +313,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Address", "label": "Address",
"read_only": 1 "read_only": 1
}, },
@@ -352,7 +352,7 @@
}, },
{ {
"fieldname": "shipping_address_display", "fieldname": "shipping_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Shipping Address", "label": "Shipping Address",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
@@ -1095,7 +1095,7 @@
}, },
{ {
"fieldname": "billing_address_display", "fieldname": "billing_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Billing Address", "label": "Billing Address",
"read_only": 1 "read_only": 1
}, },
@@ -1252,7 +1252,7 @@
"idx": 261, "idx": 261,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-03-20 16:05:31.713453", "modified": "2024-03-22 16:15:10.937188",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Purchase Receipt", "name": "Purchase Receipt",

View File

@@ -40,7 +40,7 @@ class PurchaseReceipt(BuyingController):
from erpnext.stock.doctype.purchase_receipt_item.purchase_receipt_item import PurchaseReceiptItem from erpnext.stock.doctype.purchase_receipt_item.purchase_receipt_item import PurchaseReceiptItem
additional_discount_percentage: DF.Float additional_discount_percentage: DF.Float
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
apply_discount_on: DF.Literal["", "Grand Total", "Net Total"] apply_discount_on: DF.Literal["", "Grand Total", "Net Total"]
apply_putaway_rule: DF.Check apply_putaway_rule: DF.Check
@@ -56,7 +56,7 @@ class PurchaseReceipt(BuyingController):
base_total: DF.Currency base_total: DF.Currency
base_total_taxes_and_charges: DF.Currency base_total_taxes_and_charges: DF.Currency
billing_address: DF.Link | None billing_address: DF.Link | None
billing_address_display: DF.SmallText | None billing_address_display: DF.TextEditor | None
buying_price_list: DF.Link | None buying_price_list: DF.Link | None
company: DF.Link company: DF.Link
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
@@ -109,7 +109,7 @@ class PurchaseReceipt(BuyingController):
set_posting_time: DF.Check set_posting_time: DF.Check
set_warehouse: DF.Link | None set_warehouse: DF.Link | None
shipping_address: DF.Link | None shipping_address: DF.Link | None
shipping_address_display: DF.SmallText | None shipping_address_display: DF.TextEditor | None
shipping_rule: DF.Link | None shipping_rule: DF.Link | None
status: DF.Literal["", "Draft", "To Bill", "Completed", "Return Issued", "Cancelled", "Closed"] status: DF.Literal["", "Draft", "To Bill", "Completed", "Return Issued", "Cancelled", "Closed"]
subcontracting_receipt: DF.Link | None subcontracting_receipt: DF.Link | None

View File

@@ -1081,7 +1081,9 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle
cint(frappe.user_defaults?.use_serial_batch_fields) === 1 cint(frappe.user_defaults?.use_serial_batch_fields) === 1
) { ) {
this.frm.doc.items.forEach((item) => { this.frm.doc.items.forEach((item) => {
frappe.model.set_value(item.doctype, item.name, "use_serial_batch_fields", 1); if (!item.serial_and_batch_bundle) {
frappe.model.set_value(item.doctype, item.name, "use_serial_batch_fields", 1);
}
}); });
} }
} }

View File

@@ -306,7 +306,7 @@
}, },
{ {
"fieldname": "source_address_display", "fieldname": "source_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Source Warehouse Address", "label": "Source Warehouse Address",
"read_only": 1 "read_only": 1
}, },
@@ -336,7 +336,7 @@
}, },
{ {
"fieldname": "target_address_display", "fieldname": "target_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Target Warehouse Address", "label": "Target Warehouse Address",
"read_only": 1 "read_only": 1
}, },
@@ -464,7 +464,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Address" "label": "Address"
}, },
{ {
@@ -681,7 +681,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-01-12 11:56:58.644882", "modified": "2024-03-22 16:23:13.683565",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Stock Entry", "name": "Stock Entry",

View File

@@ -85,7 +85,7 @@ class StockEntry(StockController):
add_to_transit: DF.Check add_to_transit: DF.Check
additional_costs: DF.Table[LandedCostTaxesandCharges] additional_costs: DF.Table[LandedCostTaxesandCharges]
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
apply_putaway_rule: DF.Check apply_putaway_rule: DF.Check
bom_no: DF.Link | None bom_no: DF.Link | None
@@ -127,14 +127,14 @@ class StockEntry(StockController):
scan_barcode: DF.Data | None scan_barcode: DF.Data | None
select_print_heading: DF.Link | None select_print_heading: DF.Link | None
set_posting_time: DF.Check set_posting_time: DF.Check
source_address_display: DF.SmallText | None source_address_display: DF.TextEditor | None
source_warehouse_address: DF.Link | None source_warehouse_address: DF.Link | None
stock_entry_type: DF.Link stock_entry_type: DF.Link
subcontracting_order: DF.Link | None subcontracting_order: DF.Link | None
supplier: DF.Link | None supplier: DF.Link | None
supplier_address: DF.Link | None supplier_address: DF.Link | None
supplier_name: DF.Data | None supplier_name: DF.Data | None
target_address_display: DF.SmallText | None target_address_display: DF.TextEditor | None
target_warehouse_address: DF.Link | None target_warehouse_address: DF.Link | None
to_warehouse: DF.Link | None to_warehouse: DF.Link | None
total_additional_costs: DF.Currency total_additional_costs: DF.Currency
@@ -2541,6 +2541,7 @@ class StockEntry(StockController):
) )
d.serial_and_batch_bundle = id d.serial_and_batch_bundle = id
d.use_serial_batch_fields = 0
def get_available_serial_nos(self) -> List[str]: def get_available_serial_nos(self) -> List[str]:
serial_nos = [] serial_nos = []
@@ -2640,7 +2641,9 @@ def make_stock_in_entry(source_name, target_doc=None):
def set_missing_values(source, target): def set_missing_values(source, target):
target.stock_entry_type = "Material Transfer" target.stock_entry_type = "Material Transfer"
target.set_missing_values() target.set_missing_values()
target.make_serial_and_batch_bundle_for_transfer()
if not frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields"):
target.make_serial_and_batch_bundle_for_transfer()
def update_item(source_doc, target_doc, source_parent): def update_item(source_doc, target_doc, source_parent):
target_doc.t_warehouse = "" target_doc.t_warehouse = ""

View File

@@ -825,7 +825,9 @@ def get_price_list_rate(args, item_doc, out=None):
): ):
if args.price_list and args.rate: if args.price_list and args.rate:
insert_item_price(args) insert_item_price(args)
return out
if not price_list_rate:
return out
out.price_list_rate = ( out.price_list_rate = (
flt(price_list_rate) * flt(args.plc_conversion_rate) / flt(args.conversion_rate) flt(price_list_rate) * flt(args.plc_conversion_rate) / flt(args.conversion_rate)
@@ -889,7 +891,7 @@ def insert_item_price(args):
) )
def get_item_price(args, item_code, ignore_party=False): def get_item_price(args, item_code, ignore_party=False) -> list[dict]:
""" """
Get name, price_list_rate from Item Price based on conditions Get name, price_list_rate from Item Price based on conditions
Check if the desired qty is within the increment of the packing list. Check if the desired qty is within the increment of the packing list.
@@ -911,6 +913,7 @@ def get_item_price(args, item_code, ignore_party=False):
.orderby(ip.valid_from, order=frappe.qb.desc) .orderby(ip.valid_from, order=frappe.qb.desc)
.orderby(IfNull(ip.batch_no, ""), order=frappe.qb.desc) .orderby(IfNull(ip.batch_no, ""), order=frappe.qb.desc)
.orderby(ip.uom, order=frappe.qb.desc) .orderby(ip.uom, order=frappe.qb.desc)
.limit(1)
) )
if not ignore_party: if not ignore_party:
@@ -927,7 +930,7 @@ def get_item_price(args, item_code, ignore_party=False):
& (IfNull(ip.valid_upto, "2500-12-31") >= args["transaction_date"]) & (IfNull(ip.valid_upto, "2500-12-31") >= args["transaction_date"])
) )
return query.run() return query.run(as_dict=True)
def get_price_list_rate_for(args, item_code): def get_price_list_rate_for(args, item_code):
@@ -953,7 +956,7 @@ def get_price_list_rate_for(args, item_code):
price_list_rate = get_item_price(item_price_args, item_code) price_list_rate = get_item_price(item_price_args, item_code)
if price_list_rate: if price_list_rate:
desired_qty = args.get("qty") desired_qty = args.get("qty")
if desired_qty and check_packing_list(price_list_rate[0][0], desired_qty, item_code): if desired_qty and check_packing_list(price_list_rate[0].name, desired_qty, item_code):
item_price_data = price_list_rate item_price_data = price_list_rate
else: else:
for field in ["customer", "supplier"]: for field in ["customer", "supplier"]:
@@ -973,12 +976,12 @@ def get_price_list_rate_for(args, item_code):
item_price_data = general_price_list_rate item_price_data = general_price_list_rate
if item_price_data: if item_price_data:
if item_price_data[0][2] == args.get("uom"): if item_price_data[0].uom == args.get("uom"):
return item_price_data[0][1] return item_price_data[0].price_list_rate
elif not args.get("price_list_uom_dependant"): elif not args.get("price_list_uom_dependant"):
return flt(item_price_data[0][1] * flt(args.get("conversion_factor", 1))) return flt(item_price_data[0].price_list_rate * flt(args.get("conversion_factor", 1)))
else: else:
return item_price_data[0][1] return item_price_data[0].price_list_rate
def check_packing_list(price_list_rate_name, desired_qty, item_code): def check_packing_list(price_list_rate_name, desired_qty, item_code):

View File

@@ -182,7 +182,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Supplier Address Details", "label": "Supplier Address Details",
"read_only": 1 "read_only": 1
}, },
@@ -230,7 +230,7 @@
}, },
{ {
"fieldname": "shipping_address_display", "fieldname": "shipping_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Shipping Address Details", "label": "Shipping Address Details",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
@@ -243,7 +243,7 @@
}, },
{ {
"fieldname": "billing_address_display", "fieldname": "billing_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Billing Address Details", "label": "Billing Address Details",
"read_only": 1 "read_only": 1
}, },
@@ -454,7 +454,7 @@
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-01-03 20:56:04.670380", "modified": "2024-03-22 16:15:07.797633",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Subcontracting", "module": "Subcontracting",
"name": "Subcontracting Order", "name": "Subcontracting Order",

View File

@@ -36,10 +36,10 @@ class SubcontractingOrder(SubcontractingController):
) )
additional_costs: DF.Table[LandedCostTaxesandCharges] additional_costs: DF.Table[LandedCostTaxesandCharges]
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
billing_address: DF.Link | None billing_address: DF.Link | None
billing_address_display: DF.SmallText | None billing_address_display: DF.TextEditor | None
company: DF.Link company: DF.Link
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
contact_email: DF.SmallText | None contact_email: DF.SmallText | None
@@ -59,7 +59,7 @@ class SubcontractingOrder(SubcontractingController):
set_reserve_warehouse: DF.Link | None set_reserve_warehouse: DF.Link | None
set_warehouse: DF.Link | None set_warehouse: DF.Link | None
shipping_address: DF.Link | None shipping_address: DF.Link | None
shipping_address_display: DF.SmallText | None shipping_address_display: DF.TextEditor | None
status: DF.Literal[ status: DF.Literal[
"Draft", "Draft",
"Open", "Open",

View File

@@ -192,7 +192,7 @@
}, },
{ {
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Address", "label": "Address",
"read_only": 1 "read_only": 1
}, },
@@ -231,7 +231,7 @@
}, },
{ {
"fieldname": "shipping_address_display", "fieldname": "shipping_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Shipping Address", "label": "Shipping Address",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
@@ -521,7 +521,7 @@
}, },
{ {
"fieldname": "billing_address_display", "fieldname": "billing_address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Billing Address", "label": "Billing Address",
"read_only": 1 "read_only": 1
}, },
@@ -645,7 +645,7 @@
"in_create": 1, "in_create": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2023-11-16 13:04:00.710534", "modified": "2024-03-22 16:15:08.074134",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Subcontracting", "module": "Subcontracting",
"name": "Subcontracting Receipt", "name": "Subcontracting Receipt",

View File

@@ -33,13 +33,13 @@ class SubcontractingReceipt(SubcontractingController):
) )
additional_costs: DF.Table[LandedCostTaxesandCharges] additional_costs: DF.Table[LandedCostTaxesandCharges]
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amended_from: DF.Link | None amended_from: DF.Link | None
auto_repeat: DF.Link | None auto_repeat: DF.Link | None
bill_date: DF.Date | None bill_date: DF.Date | None
bill_no: DF.Data | None bill_no: DF.Data | None
billing_address: DF.Link | None billing_address: DF.Link | None
billing_address_display: DF.SmallText | None billing_address_display: DF.TextEditor | None
company: DF.Link company: DF.Link
contact_display: DF.SmallText | None contact_display: DF.SmallText | None
contact_email: DF.SmallText | None contact_email: DF.SmallText | None
@@ -69,7 +69,7 @@ class SubcontractingReceipt(SubcontractingController):
set_posting_time: DF.Check set_posting_time: DF.Check
set_warehouse: DF.Link | None set_warehouse: DF.Link | None
shipping_address: DF.Link | None shipping_address: DF.Link | None
shipping_address_display: DF.SmallText | None shipping_address_display: DF.TextEditor | None
status: DF.Literal["", "Draft", "Completed", "Return", "Return Issued", "Cancelled", "Closed"] status: DF.Literal["", "Draft", "Completed", "Return", "Return Issued", "Cancelled", "Closed"]
supplied_items: DF.Table[SubcontractingReceiptSuppliedItem] supplied_items: DF.Table[SubcontractingReceiptSuppliedItem]
supplier: DF.Link supplier: DF.Link
@@ -149,7 +149,9 @@ class SubcontractingReceipt(SubcontractingController):
self.update_prevdoc_status() self.update_prevdoc_status()
self.set_subcontracting_order_status() self.set_subcontracting_order_status()
self.set_consumed_qty_in_subcontract_order() self.set_consumed_qty_in_subcontract_order()
self.make_bundle_using_old_serial_batch_fields()
for table_name in ["items", "supplied_items"]:
self.make_bundle_using_old_serial_batch_fields(table_name)
self.update_stock_ledger() self.update_stock_ledger()
self.make_gl_entries() self.make_gl_entries()
self.repost_future_sle_and_gle() self.repost_future_sle_and_gle()

View File

@@ -292,6 +292,7 @@ class TestSubcontractingReceipt(FrappeTestCase):
self.assertRaises(OverAllowanceError, make_return_subcontracting_receipt, **args) self.assertRaises(OverAllowanceError, make_return_subcontracting_receipt, **args)
def test_subcontracting_receipt_no_gl_entry(self): def test_subcontracting_receipt_no_gl_entry(self):
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
sco = get_subcontracting_order() sco = get_subcontracting_order()
rm_items = get_rm_items(sco.supplied_items) rm_items = get_rm_items(sco.supplied_items)
itemwise_details = make_stock_in_entry(rm_items=rm_items) itemwise_details = make_stock_in_entry(rm_items=rm_items)
@@ -327,8 +328,10 @@ class TestSubcontractingReceipt(FrappeTestCase):
# Service Cost(100 * 10) + Raw Materials Cost(100 * 10) + Additional Costs(10 * 10) = 2100 # Service Cost(100 * 10) + Raw Materials Cost(100 * 10) + Additional Costs(10 * 10) = 2100
self.assertEqual(stock_value_difference, 2100) self.assertEqual(stock_value_difference, 2100)
self.assertFalse(get_gl_entries("Subcontracting Receipt", scr.name)) self.assertFalse(get_gl_entries("Subcontracting Receipt", scr.name))
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
def test_subcontracting_receipt_gl_entry(self): def test_subcontracting_receipt_gl_entry(self):
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
sco = get_subcontracting_order( sco = get_subcontracting_order(
company="_Test Company with perpetual inventory", company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1", warehouse="Stores - TCP1",
@@ -387,6 +390,7 @@ class TestSubcontractingReceipt(FrappeTestCase):
scr.reload() scr.reload()
scr.cancel() scr.cancel()
self.assertTrue(get_gl_entries("Subcontracting Receipt", scr.name)) self.assertTrue(get_gl_entries("Subcontracting Receipt", scr.name))
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
def test_supplied_items_consumed_qty(self): def test_supplied_items_consumed_qty(self):
# Set Backflush Based On as "Material Transferred for Subcontracting" to transfer RM's more than the required qty # Set Backflush Based On as "Material Transferred for Subcontracting" to transfer RM's more than the required qty
@@ -664,6 +668,7 @@ class TestSubcontractingReceipt(FrappeTestCase):
) )
def test_subcontracting_receipt_valuation_for_fg_with_auto_created_serial_batch_bundle(self): def test_subcontracting_receipt_valuation_for_fg_with_auto_created_serial_batch_bundle(self):
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
set_backflush_based_on("BOM") set_backflush_based_on("BOM")
fg_item = make_item( fg_item = make_item(
@@ -760,9 +765,11 @@ class TestSubcontractingReceipt(FrappeTestCase):
frappe.db.set_single_value( frappe.db.set_single_value(
"Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 0 "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 0
) )
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
def test_subcontracting_receipt_raw_material_rate(self): def test_subcontracting_receipt_raw_material_rate(self):
# Step - 1: Set Backflush Based On as "BOM" # Step - 1: Set Backflush Based On as "BOM"
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
set_backflush_based_on("BOM") set_backflush_based_on("BOM")
# Step - 2: Create FG and RM Items # Step - 2: Create FG and RM Items
@@ -820,6 +827,8 @@ class TestSubcontractingReceipt(FrappeTestCase):
self.assertEqual(rm_item.rate, 100) self.assertEqual(rm_item.rate, 100)
self.assertEqual(rm_item.amount, rm_item.consumed_qty * rm_item.rate) self.assertEqual(rm_item.amount, rm_item.consumed_qty * rm_item.rate)
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
def test_quality_inspection_for_subcontracting_receipt(self): def test_quality_inspection_for_subcontracting_receipt(self):
from erpnext.stock.doctype.quality_inspection.test_quality_inspection import ( from erpnext.stock.doctype.quality_inspection.test_quality_inspection import (
create_quality_inspection, create_quality_inspection,

View File

@@ -311,7 +311,7 @@
{ {
"depends_on": "customer", "depends_on": "customer",
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"label": "Address", "label": "Address",
"read_only": 1 "read_only": 1
}, },
@@ -379,7 +379,7 @@
"icon": "fa fa-bug", "icon": "fa fa-bug",
"idx": 1, "idx": 1,
"links": [], "links": [],
"modified": "2023-11-28 17:30:35.676410", "modified": "2024-03-22 16:01:11.412114",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Support", "module": "Support",
"name": "Warranty Claim", "name": "Warranty Claim",

View File

@@ -18,7 +18,7 @@ class WarrantyClaim(TransactionBase):
if TYPE_CHECKING: if TYPE_CHECKING:
from frappe.types import DF from frappe.types import DF
address_display: DF.SmallText | None address_display: DF.TextEditor | None
amc_expiry_date: DF.Date | None amc_expiry_date: DF.Date | None
amended_from: DF.Link | None amended_from: DF.Link | None
company: DF.Link company: DF.Link

View File

@@ -176,7 +176,7 @@ def create_log(doc_name, e, from_doctype, to_doctype, status, log_date=None, res
transaction_log.from_doctype = from_doctype transaction_log.from_doctype = from_doctype
transaction_log.to_doctype = to_doctype transaction_log.to_doctype = to_doctype
transaction_log.retried = restarted transaction_log.retried = restarted
transaction_log.save() transaction_log.save(ignore_permissions=True)
def show_job_status(fail_count, deserialized_data_count, to_doctype): def show_job_status(fail_count, deserialized_data_count, to_doctype):