mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-30 12:08:26 +00:00
Merge branch 'develop' into validate-item-price-uom
This commit is contained in:
@@ -185,7 +185,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -204,7 +204,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "company_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Company Address Display",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -381,7 +381,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-06-15 15:46:53.865712",
|
||||
"modified": "2024-03-22 16:01:13.231067",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Dunning",
|
||||
|
||||
@@ -32,14 +32,14 @@ class Dunning(AccountsController):
|
||||
|
||||
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
|
||||
base_dunning_amount: DF.Currency
|
||||
body_text: DF.TextEditor | None
|
||||
closing_text: DF.TextEditor | None
|
||||
company: DF.Link
|
||||
company_address: DF.Link | None
|
||||
company_address_display: DF.SmallText | None
|
||||
company_address_display: DF.TextEditor | None
|
||||
contact_display: DF.SmallText | None
|
||||
contact_email: DF.Data | None
|
||||
contact_mobile: DF.SmallText | None
|
||||
|
||||
@@ -57,13 +57,13 @@ frappe.ui.form.on("Exchange Rate Revaluation", {
|
||||
get_entries: function (frm, account) {
|
||||
frappe.call({
|
||||
method: "get_accounts_data",
|
||||
doc: cur_frm.doc,
|
||||
doc: frm.doc,
|
||||
account: account,
|
||||
callback: function (r) {
|
||||
frappe.model.clear_table(frm.doc, "accounts");
|
||||
if (r.message) {
|
||||
r.message.forEach((d) => {
|
||||
cur_frm.add_child("accounts", d);
|
||||
frm.add_child("accounts", d);
|
||||
});
|
||||
frm.events.get_total_gain_loss(frm);
|
||||
refresh_field("accounts");
|
||||
|
||||
@@ -189,7 +189,7 @@ frappe.ui.form.on("Invoice Discounting", {
|
||||
|
||||
show_general_ledger: (frm) => {
|
||||
if (frm.doc.docstatus > 0) {
|
||||
cur_frm.add_custom_button(
|
||||
frm.add_custom_button(
|
||||
__("Accounting Ledger"),
|
||||
function () {
|
||||
frappe.route_options = {
|
||||
|
||||
@@ -255,7 +255,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -402,7 +402,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
|
||||
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");
|
||||
}
|
||||
@@ -469,11 +469,11 @@ frappe.ui.form.on("Journal Entry Account", {
|
||||
},
|
||||
|
||||
debit: function (frm, dt, dn) {
|
||||
cur_frm.cscript.update_totals(frm.doc);
|
||||
frm.cscript.update_totals(frm.doc);
|
||||
},
|
||||
|
||||
credit: function (frm, dt, dn) {
|
||||
cur_frm.cscript.update_totals(frm.doc);
|
||||
frm.cscript.update_totals(frm.doc);
|
||||
},
|
||||
|
||||
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) {
|
||||
cur_frm.cscript.update_totals(frm.doc);
|
||||
frm.cscript.update_totals(frm.doc);
|
||||
});
|
||||
|
||||
$.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))
|
||||
);
|
||||
|
||||
cur_frm.cscript.update_totals(frm.doc);
|
||||
frm.cscript.update_totals(frm.doc);
|
||||
},
|
||||
|
||||
set_exchange_rate: function (frm, cdt, cdn) {
|
||||
@@ -673,10 +673,10 @@ $.extend(erpnext.journal_entry, {
|
||||
return { filters: filters };
|
||||
},
|
||||
|
||||
reverse_journal_entry: function () {
|
||||
reverse_journal_entry: function (frm) {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry",
|
||||
frm: cur_frm,
|
||||
frm: frm,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -322,13 +322,13 @@ frappe.ui.form.on("Payment Entry", {
|
||||
"references"
|
||||
);
|
||||
|
||||
cur_frm.set_df_property(
|
||||
frm.set_df_property(
|
||||
"source_exchange_rate",
|
||||
"description",
|
||||
"1 " + frm.doc.paid_from_account_currency + " = [?] " + company_currency
|
||||
);
|
||||
|
||||
cur_frm.set_df_property(
|
||||
frm.set_df_property(
|
||||
"target_exchange_rate",
|
||||
"description",
|
||||
"1 " + frm.doc.paid_to_account_currency + " = [?] " + company_currency
|
||||
|
||||
@@ -193,7 +193,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex
|
||||
make_sales_return() {
|
||||
frappe.model.open_mapped_doc({
|
||||
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) {
|
||||
frappe.dom.unfreeze();
|
||||
cur_frm.reload_doc();
|
||||
frm.reload_doc();
|
||||
cur_pos.payment.events.submit_invoice();
|
||||
|
||||
frappe.show_alert({
|
||||
|
||||
@@ -420,7 +420,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -475,7 +475,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_address",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Shipping Address",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -489,7 +489,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "company_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 1,
|
||||
"label": "Company Address",
|
||||
"print_hide": 1,
|
||||
@@ -1563,7 +1563,7 @@
|
||||
"icon": "fa fa-file-text",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-20 16:00:34.268756",
|
||||
"modified": "2024-03-22 16:15:08.561034",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Invoice",
|
||||
|
||||
@@ -47,7 +47,7 @@ class POSInvoice(SalesInvoice):
|
||||
|
||||
account_for_change_amount: DF.Link | None
|
||||
additional_discount_percentage: DF.Float
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
advances: DF.Table[SalesInvoiceAdvance]
|
||||
against_income_account: DF.SmallText | None
|
||||
allocate_advances_automatically: DF.Check
|
||||
@@ -72,7 +72,7 @@ class POSInvoice(SalesInvoice):
|
||||
commission_rate: DF.Float
|
||||
company: DF.Link
|
||||
company_address: DF.Link | None
|
||||
company_address_display: DF.SmallText | None
|
||||
company_address_display: DF.TextEditor | None
|
||||
consolidated_invoice: DF.Link | None
|
||||
contact_display: DF.SmallText | None
|
||||
contact_email: DF.Data | None
|
||||
@@ -138,7 +138,7 @@ class POSInvoice(SalesInvoice):
|
||||
selling_price_list: DF.Link
|
||||
set_posting_time: DF.Check
|
||||
set_warehouse: DF.Link | None
|
||||
shipping_address: DF.SmallText | None
|
||||
shipping_address: DF.TextEditor | None
|
||||
shipping_address_name: DF.Link | None
|
||||
shipping_rule: DF.Link | None
|
||||
source: DF.Link | None
|
||||
|
||||
@@ -736,7 +736,6 @@ def get_pricing_rule_items(pr_doc, other_items=False) -> list:
|
||||
|
||||
def validate_coupon_code(coupon_name):
|
||||
coupon = frappe.get_doc("Coupon Code", coupon_name)
|
||||
|
||||
if coupon.valid_from:
|
||||
if coupon.valid_from > getdate(today()):
|
||||
frappe.throw(_("Sorry, this coupon code's validity has not started"))
|
||||
|
||||
@@ -131,17 +131,17 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
|
||||
|
||||
if (doc.docstatus == 1 && doc.outstanding_amount != 0 && !doc.on_hold) {
|
||||
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.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) {
|
||||
cur_frm.add_custom_button(
|
||||
this.frm.add_custom_button(
|
||||
__("Payment Request"),
|
||||
function () {
|
||||
me.make_payment_request();
|
||||
@@ -462,7 +462,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
|
||||
make_debit_note() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_debit_note",
|
||||
frm: cur_frm,
|
||||
frm: this.frm,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -443,7 +443,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -489,7 +489,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Shipping Address",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -1363,7 +1363,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "billing_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Billing Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -1638,7 +1638,7 @@
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-20 15:57:00.736868",
|
||||
"modified": "2024-03-22 16:15:09.099187",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
@@ -82,7 +82,7 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
|
||||
additional_discount_percentage: DF.Float
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
advance_tax: DF.Table[AdvanceTax]
|
||||
advances: DF.Table[PurchaseInvoiceAdvance]
|
||||
against_expense_account: DF.SmallText | None
|
||||
@@ -107,7 +107,7 @@ class PurchaseInvoice(BuyingController):
|
||||
bill_date: DF.Date | None
|
||||
bill_no: DF.Data | None
|
||||
billing_address: DF.Link | None
|
||||
billing_address_display: DF.SmallText | None
|
||||
billing_address_display: DF.TextEditor | None
|
||||
buying_price_list: DF.Link | None
|
||||
cash_bank_account: DF.Link | None
|
||||
clearance_date: DF.Date | None
|
||||
@@ -174,7 +174,7 @@ class PurchaseInvoice(BuyingController):
|
||||
set_posting_time: DF.Check
|
||||
set_warehouse: 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
|
||||
status: DF.Literal[
|
||||
"",
|
||||
|
||||
@@ -61,9 +61,9 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
||||
refresh(doc, dt, dn) {
|
||||
const me = this;
|
||||
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
|
||||
cur_frm.msgbox.hide();
|
||||
this.frm.msgbox.hide();
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
});
|
||||
|
||||
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"));
|
||||
cur_frm.page.set_inner_btn_group_as_primary(__("Create"));
|
||||
this.frm.add_custom_button(__("Return / Credit Note"), this.make_sales_return, __("Create"));
|
||||
this.frm.page.set_inner_btn_group_as_primary(__("Create"));
|
||||
}
|
||||
|
||||
if (cint(doc.update_stock) != 1) {
|
||||
// show Make Delivery Note button only if Sales Invoice is not created from Delivery Note
|
||||
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;
|
||||
});
|
||||
|
||||
if (!from_delivery_note && !is_delivered_by_supplier) {
|
||||
cur_frm.add_custom_button(
|
||||
this.frm.add_custom_button(
|
||||
__("Delivery"),
|
||||
cur_frm.cscript["Make Delivery Note"],
|
||||
this.frm.cscript["Make Delivery Note"],
|
||||
__("Create")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (doc.outstanding_amount > 0) {
|
||||
cur_frm.add_custom_button(
|
||||
this.frm.add_custom_button(
|
||||
__("Payment Request"),
|
||||
function () {
|
||||
me.make_payment_request();
|
||||
@@ -147,10 +147,10 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
||||
__("Create")
|
||||
);
|
||||
|
||||
cur_frm.add_custom_button(
|
||||
this.frm.add_custom_button(
|
||||
__("Invoice Discounting"),
|
||||
function () {
|
||||
cur_frm.events.create_invoice_discounting(cur_frm);
|
||||
this.frm.events.create_invoice_discounting(this.frm);
|
||||
},
|
||||
__("Create")
|
||||
);
|
||||
@@ -171,10 +171,10 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
||||
}
|
||||
|
||||
if (doc.docstatus === 1) {
|
||||
cur_frm.add_custom_button(
|
||||
this.frm.add_custom_button(
|
||||
__("Maintenance Schedule"),
|
||||
function () {
|
||||
cur_frm.cscript.make_maintenance_schedule();
|
||||
this.frm.cscript.make_maintenance_schedule();
|
||||
},
|
||||
__("Create")
|
||||
);
|
||||
@@ -182,7 +182,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
||||
}
|
||||
|
||||
// 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.delivery_note_btn();
|
||||
this.frm.cscript.quotation_btn();
|
||||
@@ -213,7 +213,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
||||
make_maintenance_schedule() {
|
||||
frappe.model.open_mapped_doc({
|
||||
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 to POS type or Credit Note
|
||||
if (cur_frm.doc.is_pos) {
|
||||
if (cur_frm.pos_print_format) {
|
||||
cur_frm.meta._default_print_format = cur_frm.meta.default_print_format;
|
||||
cur_frm.meta.default_print_format = cur_frm.pos_print_format;
|
||||
if (this.frm.doc.is_pos) {
|
||||
if (this.frm.pos_print_format) {
|
||||
this.frm.meta._default_print_format = this.frm.meta.default_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) {
|
||||
if (cur_frm.return_print_format) {
|
||||
cur_frm.meta._default_print_format = cur_frm.meta.default_print_format;
|
||||
cur_frm.meta.default_print_format = cur_frm.return_print_format;
|
||||
} else if (this.frm.doc.is_return && !this.frm.meta.default_print_format) {
|
||||
if (this.frm.return_print_format) {
|
||||
this.frm.meta._default_print_format = this.frm.meta.default_print_format;
|
||||
this.frm.meta.default_print_format = this.frm.return_print_format;
|
||||
}
|
||||
} else {
|
||||
if (cur_frm.meta._default_print_format) {
|
||||
cur_frm.meta.default_print_format = cur_frm.meta._default_print_format;
|
||||
cur_frm.meta._default_print_format = null;
|
||||
if (this.frm.meta._default_print_format) {
|
||||
this.frm.meta.default_print_format = this.frm.meta._default_print_format;
|
||||
this.frm.meta._default_print_format = null;
|
||||
} else if (
|
||||
in_list(
|
||||
[cur_frm.pos_print_format, cur_frm.return_print_format],
|
||||
cur_frm.meta.default_print_format
|
||||
[this.frm.pos_print_format, this.frm.return_print_format].includes(
|
||||
this.frm.meta.default_print_format
|
||||
)
|
||||
) {
|
||||
cur_frm.meta.default_print_format = null;
|
||||
cur_frm.meta._default_print_format = null;
|
||||
this.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() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return",
|
||||
frm: cur_frm,
|
||||
frm: this.frm,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
"section_break_49",
|
||||
"apply_discount_on",
|
||||
"base_discount_amount",
|
||||
"coupon_code",
|
||||
"is_cash_or_non_trade_discount",
|
||||
"additional_discount_account",
|
||||
"column_break_51",
|
||||
@@ -494,7 +495,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Address",
|
||||
@@ -565,7 +566,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_address",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Shipping Address",
|
||||
@@ -583,7 +584,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "company_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Company Address",
|
||||
@@ -1998,7 +1999,7 @@
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "dispatch_address",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Dispatch Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -2173,6 +2174,12 @@
|
||||
"label": "Don't Create Loyalty Points",
|
||||
"no_copy": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "coupon_code",
|
||||
"fieldtype": "Link",
|
||||
"label": "Coupon Code",
|
||||
"options": "Coupon Code"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "eval: doc.is_return && doc.return_against",
|
||||
@@ -2180,7 +2187,8 @@
|
||||
"fieldname": "update_outstanding_for_self",
|
||||
"fieldtype": "Check",
|
||||
"label": "Update Outstanding for Self",
|
||||
"no_copy": 1
|
||||
"no_copy": 1,
|
||||
"print_hide": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
@@ -2193,7 +2201,7 @@
|
||||
"link_fieldname": "consolidated_invoice"
|
||||
}
|
||||
],
|
||||
"modified": "2024-03-20 16:02:52.237732",
|
||||
"modified": "2024-03-22 17:50:34.395602",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
@@ -2248,4 +2256,4 @@
|
||||
"title_field": "title",
|
||||
"track_changes": 1,
|
||||
"track_seen": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
|
||||
get_loyalty_program_details_with_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 (
|
||||
validate_docs_for_deferred_accounting,
|
||||
validate_docs_for_voucher_types,
|
||||
@@ -73,7 +77,7 @@ class SalesInvoice(SellingController):
|
||||
account_for_change_amount: DF.Link | None
|
||||
additional_discount_account: DF.Link | None
|
||||
additional_discount_percentage: DF.Float
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
advances: DF.Table[SalesInvoiceAdvance]
|
||||
against_income_account: DF.SmallText | None
|
||||
allocate_advances_automatically: DF.Check
|
||||
@@ -98,7 +102,7 @@ class SalesInvoice(SellingController):
|
||||
commission_rate: DF.Float
|
||||
company: DF.Link
|
||||
company_address: DF.Link | None
|
||||
company_address_display: DF.SmallText | None
|
||||
company_address_display: DF.TextEditor | None
|
||||
company_tax_id: DF.Data | None
|
||||
contact_display: DF.SmallText | None
|
||||
contact_email: DF.Data | None
|
||||
@@ -106,6 +110,7 @@ class SalesInvoice(SellingController):
|
||||
contact_person: DF.Link | None
|
||||
conversion_rate: DF.Float
|
||||
cost_center: DF.Link | None
|
||||
coupon_code: DF.Link | None
|
||||
currency: DF.Link
|
||||
customer: DF.Link | None
|
||||
customer_address: DF.Link | None
|
||||
@@ -114,7 +119,7 @@ class SalesInvoice(SellingController):
|
||||
debit_to: DF.Link
|
||||
disable_rounded_total: DF.Check
|
||||
discount_amount: DF.Currency
|
||||
dispatch_address: DF.SmallText | None
|
||||
dispatch_address: DF.TextEditor | None
|
||||
dispatch_address_name: DF.Link | None
|
||||
dont_create_loyalty_points: DF.Check
|
||||
due_date: DF.Date | None
|
||||
@@ -178,7 +183,7 @@ class SalesInvoice(SellingController):
|
||||
set_posting_time: DF.Check
|
||||
set_target_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_rule: 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
|
||||
)
|
||||
|
||||
# Validating coupon code
|
||||
if self.coupon_code:
|
||||
validate_coupon_code(self.coupon_code)
|
||||
|
||||
if cint(self.is_pos):
|
||||
self.validate_pos()
|
||||
|
||||
@@ -473,6 +482,9 @@ class SalesInvoice(SellingController):
|
||||
self.update_project()
|
||||
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
|
||||
if (
|
||||
not self.is_return
|
||||
@@ -563,6 +575,9 @@ class SalesInvoice(SellingController):
|
||||
self.db_set("status", "Cancelled")
|
||||
self.db_set("repost_required", 0)
|
||||
|
||||
if self.coupon_code:
|
||||
update_coupon_code_count(self.coupon_code, "cancelled")
|
||||
|
||||
if (
|
||||
frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction"
|
||||
):
|
||||
|
||||
@@ -669,20 +669,20 @@ class GrossProfitGenerator(object):
|
||||
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)
|
||||
if incoming_amount:
|
||||
return incoming_amount
|
||||
return flt(row.qty) * incoming_amount
|
||||
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)
|
||||
|
||||
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")
|
||||
|
||||
query = (
|
||||
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.item_code == item_code)
|
||||
.where(delivery_note_item.against_sales_order == sales_order)
|
||||
|
||||
@@ -460,3 +460,95 @@ class TestGrossProfit(FrappeTestCase):
|
||||
}
|
||||
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
|
||||
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])
|
||||
|
||||
@@ -24,3 +24,10 @@ frappe.query_reports["Profit and Loss Statement"]["filters"].push({
|
||||
fieldtype: "Check",
|
||||
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,
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@ frappe.ui.form.on("Asset", {
|
||||
method: "erpnext.assets.doctype.asset.asset.make_asset_movement",
|
||||
freeze: true,
|
||||
args: {
|
||||
assets: [{ name: cur_frm.doc.name }],
|
||||
assets: [{ name: frm.doc.name }],
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.message) {
|
||||
@@ -791,9 +791,7 @@ erpnext.asset.scrap_asset = function (frm) {
|
||||
asset_name: frm.doc.name,
|
||||
},
|
||||
method: "erpnext.assets.doctype.asset.depreciation.scrap_asset",
|
||||
callback: function (r) {
|
||||
cur_frm.reload_doc();
|
||||
},
|
||||
callback: (r) => frm.reload_doc(),
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -805,19 +803,17 @@ erpnext.asset.restore_asset = function (frm) {
|
||||
asset_name: frm.doc.name,
|
||||
},
|
||||
method: "erpnext.assets.doctype.asset.depreciation.restore_asset",
|
||||
callback: function (r) {
|
||||
cur_frm.reload_doc();
|
||||
},
|
||||
callback: (r) => frm.reload_doc(),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
erpnext.asset.transfer_asset = function () {
|
||||
erpnext.asset.transfer_asset = function (frm) {
|
||||
frappe.call({
|
||||
method: "erpnext.assets.doctype.asset.asset.make_asset_movement",
|
||||
freeze: true,
|
||||
args: {
|
||||
assets: [{ name: cur_frm.doc.name }],
|
||||
assets: [{ name: frm.doc.name }],
|
||||
purpose: "Transfer",
|
||||
},
|
||||
callback: function (r) {
|
||||
|
||||
@@ -86,7 +86,7 @@ frappe.ui.form.on("Purchase Order", {
|
||||
args: {
|
||||
subcontract_order: frm.doc.name,
|
||||
rm_details: po_details,
|
||||
order_doctype: cur_frm.doc.doctype,
|
||||
order_doctype: frm.doc.doctype,
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r && r.message) {
|
||||
@@ -270,8 +270,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
var allow_receipt = false;
|
||||
var is_drop_ship = false;
|
||||
|
||||
for (var i in cur_frm.doc.items) {
|
||||
var item = cur_frm.doc.items[i];
|
||||
for (var i in this.frm.doc.items) {
|
||||
var item = this.frm.doc.items[i];
|
||||
if (item.delivered_by_supplier !== 1) {
|
||||
allow_receipt = true;
|
||||
} else {
|
||||
@@ -348,7 +348,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
if (doc.status != "Closed") {
|
||||
if (doc.status != "On Hold") {
|
||||
if (flt(doc.per_received, 2) < 100 && allow_receipt) {
|
||||
cur_frm.add_custom_button(
|
||||
this.frm.add_custom_button(
|
||||
__("Purchase Receipt"),
|
||||
this.make_purchase_receipt,
|
||||
__("Create")
|
||||
@@ -356,7 +356,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
if (doc.is_subcontracted) {
|
||||
if (doc.is_old_subcontracting_flow) {
|
||||
if (me.has_unsupplied_items()) {
|
||||
cur_frm.add_custom_button(
|
||||
this.frm.add_custom_button(
|
||||
__("Material to Supplier"),
|
||||
function () {
|
||||
me.make_stock_entry();
|
||||
@@ -365,7 +365,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
);
|
||||
}
|
||||
} else {
|
||||
cur_frm.add_custom_button(
|
||||
this.frm.add_custom_button(
|
||||
__("Subcontracting Order"),
|
||||
this.make_subcontracting_order,
|
||||
__("Create")
|
||||
@@ -374,7 +374,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
}
|
||||
}
|
||||
if (flt(doc.per_billed, 2) < 100)
|
||||
cur_frm.add_custom_button(
|
||||
this.frm.add_custom_button(
|
||||
__("Purchase Invoice"),
|
||||
this.make_purchase_invoice,
|
||||
__("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) {
|
||||
cur_frm.cscript.add_from_mappers();
|
||||
this.frm.cscript.add_from_mappers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,8 +458,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
frappe.call({
|
||||
method: "erpnext.controllers.subcontracting_controller.make_rm_stock_entry",
|
||||
args: {
|
||||
subcontract_order: cur_frm.doc.name,
|
||||
order_doctype: cur_frm.doc.doctype,
|
||||
subcontract_order: this.frm.doc.name,
|
||||
order_doctype: this.frm.doc.doctype,
|
||||
},
|
||||
callback: function (r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
@@ -478,7 +478,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
make_purchase_receipt() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt",
|
||||
frm: cur_frm,
|
||||
frm: this.frm,
|
||||
freeze_message: __("Creating Purchase Receipt ..."),
|
||||
});
|
||||
}
|
||||
@@ -486,14 +486,14 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
make_purchase_invoice() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice",
|
||||
frm: cur_frm,
|
||||
frm: this.frm,
|
||||
});
|
||||
}
|
||||
|
||||
make_subcontracting_order() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_subcontracting_order",
|
||||
frm: cur_frm,
|
||||
frm: this.frm,
|
||||
freeze_message: __("Creating Subcontracting Order ..."),
|
||||
});
|
||||
}
|
||||
@@ -652,7 +652,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
}
|
||||
|
||||
unhold_purchase_order() {
|
||||
cur_frm.cscript.update_status("Resume", "Draft");
|
||||
this.frm.cscript.update_status("Resume", "Draft");
|
||||
}
|
||||
|
||||
hold_purchase_order() {
|
||||
@@ -692,15 +692,15 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
}
|
||||
|
||||
unclose_purchase_order() {
|
||||
cur_frm.cscript.update_status("Re-open", "Submitted");
|
||||
this.frm.cscript.update_status("Re-open", "Submitted");
|
||||
}
|
||||
|
||||
close_purchase_order() {
|
||||
cur_frm.cscript.update_status("Close", "Closed");
|
||||
this.frm.cscript.update_status("Close", "Closed");
|
||||
}
|
||||
|
||||
delivered_by_supplier() {
|
||||
cur_frm.cscript.update_status("Deliver", "Delivered");
|
||||
this.frm.cscript.update_status("Deliver", "Delivered");
|
||||
}
|
||||
|
||||
items_on_form_rendered() {
|
||||
|
||||
@@ -355,7 +355,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Supplier Address Details",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -394,7 +394,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Shipping Address Details",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -1098,7 +1098,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "billing_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Billing Address Details",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -1288,7 +1288,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-20 16:03:31.611808",
|
||||
"modified": "2024-03-22 16:15:09.674963",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
||||
@@ -55,7 +55,7 @@ class PurchaseOrder(BuyingController):
|
||||
)
|
||||
|
||||
additional_discount_percentage: DF.Float
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
advance_paid: DF.Currency
|
||||
advance_payment_status: DF.Literal["Not Initiated", "Initiated", "Partially Paid", "Fully Paid"]
|
||||
amended_from: DF.Link | None
|
||||
@@ -74,7 +74,7 @@ class PurchaseOrder(BuyingController):
|
||||
base_total: DF.Currency
|
||||
base_total_taxes_and_charges: DF.Currency
|
||||
billing_address: DF.Link | None
|
||||
billing_address_display: DF.SmallText | None
|
||||
billing_address_display: DF.TextEditor | None
|
||||
buying_price_list: DF.Link | None
|
||||
company: DF.Link
|
||||
contact_display: DF.SmallText | None
|
||||
@@ -131,7 +131,7 @@ class PurchaseOrder(BuyingController):
|
||||
set_reserve_warehouse: DF.Link | None
|
||||
set_warehouse: 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
|
||||
status: DF.Literal[
|
||||
"",
|
||||
|
||||
@@ -303,7 +303,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "billing_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Billing Address Details",
|
||||
"read_only": 1
|
||||
}
|
||||
@@ -312,7 +312,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-11-06 12:45:28.898706",
|
||||
"modified": "2024-03-22 16:01:19.097788",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Request for Quotation",
|
||||
|
||||
@@ -40,7 +40,7 @@ class RequestforQuotation(BuyingController):
|
||||
|
||||
amended_from: DF.Link | None
|
||||
billing_address: DF.Link | None
|
||||
billing_address_display: DF.SmallText | None
|
||||
billing_address_display: DF.TextEditor | None
|
||||
company: DF.Link
|
||||
email_template: DF.Link | None
|
||||
incoterm: DF.Link | None
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
if (this.frm.doc.docstatus === 1) {
|
||||
cur_frm.add_custom_button(__("Purchase Order"), this.make_purchase_order, __("Create"));
|
||||
cur_frm.page.set_inner_btn_group_as_primary(__("Create"));
|
||||
cur_frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create"));
|
||||
this.frm.add_custom_button(__("Purchase Order"), this.make_purchase_order, __("Create"));
|
||||
this.frm.page.set_inner_btn_group_as_primary(__("Create"));
|
||||
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create"));
|
||||
} else if (this.frm.doc.docstatus === 0) {
|
||||
this.frm.add_custom_button(
|
||||
__("Material Request"),
|
||||
@@ -87,13 +87,13 @@ erpnext.buying.SupplierQuotationController = class SupplierQuotationController e
|
||||
make_purchase_order() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_purchase_order",
|
||||
frm: cur_frm,
|
||||
frm: this.frm,
|
||||
});
|
||||
}
|
||||
make_quotation() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_quotation",
|
||||
frm: cur_frm,
|
||||
frm: this.frm,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -228,7 +228,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -865,7 +865,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Shipping Address Details",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -897,7 +897,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "billing_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Billing Address Details",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -928,7 +928,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-20 16:03:59.069145",
|
||||
"modified": "2024-03-22 16:15:10.122197",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation",
|
||||
|
||||
@@ -31,7 +31,7 @@ class SupplierQuotation(BuyingController):
|
||||
)
|
||||
|
||||
additional_discount_percentage: DF.Float
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amended_from: DF.Link | None
|
||||
apply_discount_on: DF.Literal["", "Grand Total", "Net Total"]
|
||||
auto_repeat: DF.Link | None
|
||||
@@ -46,7 +46,7 @@ class SupplierQuotation(BuyingController):
|
||||
base_total: DF.Currency
|
||||
base_total_taxes_and_charges: DF.Currency
|
||||
billing_address: DF.Link | None
|
||||
billing_address_display: DF.SmallText | None
|
||||
billing_address_display: DF.TextEditor | None
|
||||
buying_price_list: DF.Link | None
|
||||
company: DF.Link
|
||||
contact_display: DF.SmallText | None
|
||||
@@ -81,7 +81,7 @@ class SupplierQuotation(BuyingController):
|
||||
rounding_adjustment: DF.Currency
|
||||
select_print_heading: 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
|
||||
status: DF.Literal["", "Draft", "Submitted", "Stopped", "Cancelled", "Expired"]
|
||||
supplier: DF.Link
|
||||
|
||||
@@ -380,6 +380,12 @@ class AccountsController(TransactionBase):
|
||||
for bundle in bundles:
|
||||
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):
|
||||
if (
|
||||
self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against
|
||||
|
||||
@@ -48,7 +48,9 @@ class StockController(AccountsController):
|
||||
super(StockController, self).validate()
|
||||
|
||||
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"):
|
||||
self.validate_inspection()
|
||||
self.validate_serialized_batch()
|
||||
@@ -58,12 +60,19 @@ class StockController(AccountsController):
|
||||
self.validate_internal_transfer()
|
||||
self.validate_putaway_capacity()
|
||||
|
||||
def validate_duplicate_serial_and_batch_bundle(self):
|
||||
if sbb_list := [
|
||||
item.get("serial_and_batch_bundle")
|
||||
for item in self.items
|
||||
if item.get("serial_and_batch_bundle")
|
||||
]:
|
||||
def validate_duplicate_serial_and_batch_bundle(self, table_name):
|
||||
if not self.get(table_name):
|
||||
return
|
||||
|
||||
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")
|
||||
data = (
|
||||
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")
|
||||
):
|
||||
bundle_details = {
|
||||
"item_code": row.item_code,
|
||||
"item_code": row.get("rm_item_code") or row.item_code,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"voucher_type": self.doctype,
|
||||
@@ -200,7 +209,7 @@ class StockController(AccountsController):
|
||||
"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.create_serial_batch_bundle(bundle_details, row)
|
||||
|
||||
@@ -219,6 +228,12 @@ class StockController(AccountsController):
|
||||
type_of_transaction = "Inward"
|
||||
if not self.is_return:
|
||||
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:
|
||||
type_of_transaction = get_type_of_transaction(self, row)
|
||||
|
||||
@@ -550,13 +565,30 @@ class StockController(AccountsController):
|
||||
)
|
||||
|
||||
def delete_auto_created_batches(self):
|
||||
for row in self.items:
|
||||
if row.serial_and_batch_bundle:
|
||||
frappe.db.set_value(
|
||||
"Serial and Batch Bundle", row.serial_and_batch_bundle, {"is_cancelled": 1}
|
||||
)
|
||||
for table_name in ["items", "packed_items", "supplied_items"]:
|
||||
if not self.get(table_name):
|
||||
continue
|
||||
|
||||
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):
|
||||
if not table_name:
|
||||
|
||||
@@ -379,10 +379,10 @@ class SubcontractingController(StockController):
|
||||
if 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
|
||||
|
||||
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_data = voucher_bundle_data.get(bundle_key, frappe._dict())
|
||||
@@ -392,6 +392,9 @@ class SubcontractingController(StockController):
|
||||
|
||||
if bundle_data.batch_nos:
|
||||
for batch_no, qty in bundle_data.batch_nos.items():
|
||||
if qty < 0:
|
||||
qty = abs(qty)
|
||||
|
||||
if qty > 0:
|
||||
details.batch_no[batch_no] += qty
|
||||
bundle_data.batch_nos[batch_no] -= qty
|
||||
@@ -545,17 +548,24 @@ class SubcontractingController(StockController):
|
||||
|
||||
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:
|
||||
rm_obj.required_qty = qty
|
||||
rm_obj.amount = rm_obj.required_qty * rm_obj.rate
|
||||
else:
|
||||
rm_obj.consumed_qty = qty
|
||||
rm_obj.required_qty = bom_item.required_qty or qty
|
||||
rm_obj.serial_and_batch_bundle = None
|
||||
setattr(
|
||||
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(
|
||||
{
|
||||
"item_code": rm_obj.rm_item_code,
|
||||
@@ -581,6 +591,68 @@ class SubcontractingController(StockController):
|
||||
|
||||
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):
|
||||
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"),
|
||||
"main_item_code": fg_item_code,
|
||||
"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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ class TestSubcontractingController(FrappeTestCase):
|
||||
- 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")
|
||||
service_items = [
|
||||
{
|
||||
@@ -202,6 +203,8 @@ class TestSubcontractingController(FrappeTestCase):
|
||||
if value.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):
|
||||
"""
|
||||
- Set backflush based on Material Transfer.
|
||||
@@ -211,6 +214,7 @@ class TestSubcontractingController(FrappeTestCase):
|
||||
- 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")
|
||||
service_items = [
|
||||
{
|
||||
@@ -278,6 +282,8 @@ class TestSubcontractingController(FrappeTestCase):
|
||||
self.assertEqual(value.qty, 6)
|
||||
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):
|
||||
"""
|
||||
- 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.
|
||||
"""
|
||||
|
||||
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
|
||||
set_backflush_based_on("Material Transferred for Subcontract")
|
||||
service_items = [
|
||||
{
|
||||
@@ -333,6 +340,7 @@ class TestSubcontractingController(FrappeTestCase):
|
||||
get_serial_nos(doc.items[0].serial_no),
|
||||
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):
|
||||
"""
|
||||
@@ -578,6 +586,7 @@ class TestSubcontractingController(FrappeTestCase):
|
||||
- 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")
|
||||
service_items = [
|
||||
{
|
||||
@@ -643,6 +652,8 @@ class TestSubcontractingController(FrappeTestCase):
|
||||
self.assertEqual(value.qty, details.qty)
|
||||
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):
|
||||
"""
|
||||
- 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.
|
||||
"""
|
||||
|
||||
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
|
||||
serial_no = "ABC"
|
||||
if not frappe.db.exists("Serial No", serial_no):
|
||||
frappe.get_doc(
|
||||
@@ -712,6 +724,7 @@ class TestSubcontractingController(FrappeTestCase):
|
||||
scr1.save()
|
||||
self.delete_bundle_from_scr(scr1)
|
||||
scr1.delete()
|
||||
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
|
||||
|
||||
@staticmethod
|
||||
def delete_bundle_from_scr(scr):
|
||||
@@ -844,6 +857,223 @@ class TestSubcontractingController(FrappeTestCase):
|
||||
for item in sco.get("supplied_items"):
|
||||
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):
|
||||
item_dict = {}
|
||||
@@ -914,6 +1144,7 @@ def update_item_details(child_row, details):
|
||||
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:
|
||||
doc = frappe.get_doc("Serial and Batch Bundle", child_row.serial_and_batch_bundle)
|
||||
for row in doc.get("entries"):
|
||||
@@ -945,6 +1176,7 @@ def make_stock_transfer_entry(**args):
|
||||
"rate": row.rate or 100,
|
||||
"stock_uom": row.stock_uom or "Nos",
|
||||
"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)
|
||||
@@ -960,9 +1192,12 @@ def make_stock_transfer_entry(**args):
|
||||
if batch_qty >= row.qty:
|
||||
batches[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
|
||||
|
||||
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(
|
||||
frappe._dict(
|
||||
{
|
||||
@@ -978,6 +1213,9 @@ def make_stock_transfer_entry(**args):
|
||||
)
|
||||
).name
|
||||
|
||||
if serial_nos and row.get("use_serial_batch_fields"):
|
||||
item["serial_no"] = "\n".join(serial_nos)
|
||||
|
||||
items.append(item)
|
||||
|
||||
ste_dict = make_rm_stock_entry(args.sco_no, items)
|
||||
@@ -1132,6 +1370,7 @@ def get_rm_items(supplied_items):
|
||||
"rate": item.rate,
|
||||
"stock_uom": item.stock_uom,
|
||||
"warehouse": item.reserve_warehouse,
|
||||
"use_serial_batch_fields": 0,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ frappe.ui.form.on("Campaign", {
|
||||
frappe.boot.sysdefaults.campaign_naming_by == "Naming Series"
|
||||
);
|
||||
} else {
|
||||
cur_frm.add_custom_button(
|
||||
frm.add_custom_button(
|
||||
__("View Leads"),
|
||||
function () {
|
||||
frappe.route_options = { source: "Campaign", campaign_name: frm.doc.name };
|
||||
|
||||
@@ -89,32 +89,33 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller
|
||||
make_customer() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_customer",
|
||||
frm: cur_frm,
|
||||
frm: this.frm,
|
||||
});
|
||||
}
|
||||
|
||||
make_quotation() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_quotation",
|
||||
frm: cur_frm,
|
||||
frm: this.frm,
|
||||
});
|
||||
}
|
||||
|
||||
make_prospect() {
|
||||
const me = this;
|
||||
frappe.model.with_doctype("Prospect", function () {
|
||||
let prospect = frappe.model.get_new_doc("Prospect");
|
||||
prospect.company_name = cur_frm.doc.company_name;
|
||||
prospect.no_of_employees = cur_frm.doc.no_of_employees;
|
||||
prospect.industry = cur_frm.doc.industry;
|
||||
prospect.market_segment = cur_frm.doc.market_segment;
|
||||
prospect.territory = cur_frm.doc.territory;
|
||||
prospect.fax = cur_frm.doc.fax;
|
||||
prospect.website = cur_frm.doc.website;
|
||||
prospect.prospect_owner = cur_frm.doc.lead_owner;
|
||||
prospect.notes = cur_frm.doc.notes;
|
||||
prospect.company_name = me.frm.doc.company_name;
|
||||
prospect.no_of_employees = me.frm.doc.no_of_employees;
|
||||
prospect.industry = me.frm.doc.industry;
|
||||
prospect.market_segment = me.frm.doc.market_segment;
|
||||
prospect.territory = me.frm.doc.territory;
|
||||
prospect.fax = me.frm.doc.fax;
|
||||
prospect.website = me.frm.doc.website;
|
||||
prospect.prospect_owner = me.frm.doc.lead_owner;
|
||||
prospect.notes = me.frm.doc.notes;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -318,14 +318,14 @@ erpnext.crm.Opportunity = class Opportunity extends frappe.ui.form.Controller {
|
||||
create_quotation() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation",
|
||||
frm: cur_frm,
|
||||
frm: this.frm,
|
||||
});
|
||||
}
|
||||
|
||||
make_customer() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.opportunity.opportunity.make_customer",
|
||||
frm: cur_frm,
|
||||
frm: this.frm,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -250,7 +250,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 1,
|
||||
"label": "Address",
|
||||
"oldfieldname": "address",
|
||||
@@ -622,7 +622,7 @@
|
||||
"icon": "fa fa-info-sign",
|
||||
"idx": 195,
|
||||
"links": [],
|
||||
"modified": "2022-10-13 12:42:21.545636",
|
||||
"modified": "2024-03-22 16:01:10.721453",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Opportunity",
|
||||
|
||||
@@ -40,7 +40,7 @@ class Opportunity(TransactionBase, CRMNote):
|
||||
OpportunityLostReasonDetail,
|
||||
)
|
||||
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amended_from: DF.Link | None
|
||||
annual_revenue: DF.Currency
|
||||
base_opportunity_amount: DF.Currency
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 1,
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
@@ -238,7 +238,7 @@
|
||||
"link_fieldname": "maintenance_schedule"
|
||||
}
|
||||
],
|
||||
"modified": "2023-06-03 16:15:43.958072",
|
||||
"modified": "2024-03-22 16:01:11.868813",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Maintenance",
|
||||
"name": "Maintenance Schedule",
|
||||
|
||||
@@ -26,7 +26,7 @@ class MaintenanceSchedule(TransactionBase):
|
||||
MaintenanceScheduleItem,
|
||||
)
|
||||
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amended_from: DF.Link | None
|
||||
company: DF.Link
|
||||
contact_display: DF.SmallText | None
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 1,
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
@@ -295,7 +295,7 @@
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-06-03 16:19:07.902723",
|
||||
"modified": "2024-03-22 16:01:12.354826",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Maintenance",
|
||||
"name": "Maintenance Visit",
|
||||
|
||||
@@ -22,7 +22,7 @@ class MaintenanceVisit(TransactionBase):
|
||||
MaintenanceVisitPurpose,
|
||||
)
|
||||
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amended_from: DF.Link | None
|
||||
company: DF.Link
|
||||
completion_status: DF.Literal["", "Partially Completed", "Fully Completed"]
|
||||
|
||||
@@ -1208,6 +1208,51 @@ class TestWorkOrder(FrappeTestCase):
|
||||
except frappe.MandatoryError:
|
||||
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):
|
||||
serial_nos = []
|
||||
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.transfer_material_against = args.transfer_material_against or "Work Order"
|
||||
wo_order.from_wip_warehouse = args.from_wip_warehouse or 0
|
||||
wo_order.batch_size = args.batch_size or 0
|
||||
|
||||
if args.source_warehouse:
|
||||
for item in wo_order.get("required_items"):
|
||||
|
||||
@@ -536,6 +536,12 @@ class WorkOrder(Document):
|
||||
"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 = []
|
||||
if item_details.serial_no_series:
|
||||
serial_nos = get_available_serial_nos(item_details.serial_no_series, self.qty)
|
||||
@@ -556,10 +562,20 @@ class WorkOrder(Document):
|
||||
"description",
|
||||
"status",
|
||||
"work_order",
|
||||
"batch_no",
|
||||
]
|
||||
|
||||
serial_nos_details = []
|
||||
index = 0
|
||||
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_no,
|
||||
@@ -574,6 +590,7 @@ class WorkOrder(Document):
|
||||
item_details.description,
|
||||
"Inactive",
|
||||
self.name,
|
||||
batch_no,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -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.allow_on_submit_dimensions_for_repostable_doctypes
|
||||
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
|
||||
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
|
||||
erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20
|
||||
|
||||
@@ -12,6 +12,10 @@ def execute():
|
||||
creation_date = "2024-01-25"
|
||||
|
||||
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 := (
|
||||
qb.from_(si)
|
||||
.select(si.name)
|
||||
@@ -37,6 +41,10 @@ def execute():
|
||||
).run()
|
||||
|
||||
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 := (
|
||||
qb.from_(pi)
|
||||
.select(pi.name)
|
||||
|
||||
@@ -2267,9 +2267,9 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
}
|
||||
|
||||
get_method_for_payment() {
|
||||
var 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(['Sales Invoice', 'Purchase Invoice'].includes( cur_frm.doc.doctype)){
|
||||
let method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
|
||||
if(this.frm.doc.__onload && this.frm.doc.__onload.make_payment_via_journal_entry){
|
||||
if(['Sales Invoice', 'Purchase Invoice'].includes( this.frm.doc.doctype)){
|
||||
method = "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice";
|
||||
}else {
|
||||
method= "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order";
|
||||
|
||||
@@ -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
|
||||
$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();
|
||||
grid_row.doc.completed = 1;
|
||||
grid_row.doc.activity_type = args.activity_type;
|
||||
|
||||
@@ -549,6 +549,14 @@ body[data-route="pos"] {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 1,
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
@@ -238,7 +238,7 @@
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-02-04 18:20:12.020313",
|
||||
"modified": "2024-03-22 16:01:13.513355",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Installation Note",
|
||||
@@ -271,4 +271,4 @@
|
||||
"states": [],
|
||||
"timeline_field": "customer",
|
||||
"title_field": "customer_name"
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ class InstallationNote(TransactionBase):
|
||||
InstallationNoteItem,
|
||||
)
|
||||
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amended_from: DF.Link | None
|
||||
company: DF.Link
|
||||
contact_display: DF.SmallText | None
|
||||
@@ -38,6 +38,7 @@ class InstallationNote(TransactionBase):
|
||||
inst_time: DF.Time | None
|
||||
items: DF.Table[InstallationNoteItem]
|
||||
naming_series: DF.Literal["MAT-INS-.YYYY.-"]
|
||||
project: DF.Link | None
|
||||
remarks: DF.SmallText | None
|
||||
status: DF.Literal["Draft", "Submitted", "Cancelled"]
|
||||
territory: DF.Link
|
||||
|
||||
@@ -265,7 +265,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Address",
|
||||
"oldfieldname": "customer_address",
|
||||
"oldfieldtype": "Small Text",
|
||||
@@ -318,7 +318,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_address",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Shipping Address",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -965,7 +965,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "company_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Company Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -1073,7 +1073,7 @@
|
||||
"idx": 82,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-20 16:04:21.567847",
|
||||
"modified": "2024-03-22 16:15:10.488656",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Quotation",
|
||||
|
||||
@@ -34,7 +34,7 @@ class Quotation(SellingController):
|
||||
from erpnext.stock.doctype.packed_item.packed_item import PackedItem
|
||||
|
||||
additional_discount_percentage: DF.Float
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amended_from: DF.Link | None
|
||||
apply_discount_on: DF.Literal["", "Grand Total", "Net Total"]
|
||||
auto_repeat: DF.Link | None
|
||||
@@ -49,7 +49,7 @@ class Quotation(SellingController):
|
||||
campaign: DF.Link | None
|
||||
company: DF.Link
|
||||
company_address: DF.Link | None
|
||||
company_address_display: DF.SmallText | None
|
||||
company_address_display: DF.TextEditor | None
|
||||
competitors: DF.TableMultiSelect[CompetitorDetail]
|
||||
contact_display: DF.SmallText | None
|
||||
contact_email: DF.Data | None
|
||||
@@ -93,7 +93,7 @@ class Quotation(SellingController):
|
||||
scan_barcode: DF.Data | None
|
||||
select_print_heading: DF.Link | None
|
||||
selling_price_list: DF.Link
|
||||
shipping_address: DF.SmallText | None
|
||||
shipping_address: DF.TextEditor | None
|
||||
shipping_address_name: DF.Link | None
|
||||
shipping_rule: DF.Link | None
|
||||
source: DF.Link | None
|
||||
|
||||
@@ -369,7 +369,7 @@
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Address",
|
||||
@@ -415,7 +415,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "company_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Company Address",
|
||||
@@ -450,7 +450,7 @@
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "shipping_address",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Shipping Address",
|
||||
@@ -1525,7 +1525,7 @@
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "dispatch_address_name",
|
||||
"fieldname": "dispatch_address",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Dispatch Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -1657,7 +1657,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-20 16:04:43.627183",
|
||||
"modified": "2024-03-22 16:15:04.884816",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order",
|
||||
|
||||
@@ -64,7 +64,7 @@ class SalesOrder(SellingController):
|
||||
from erpnext.stock.doctype.packed_item.packed_item import PackedItem
|
||||
|
||||
additional_discount_percentage: DF.Float
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
advance_paid: DF.Currency
|
||||
advance_payment_status: DF.Literal["Not Requested", "Requested", "Partially Paid", "Fully Paid"]
|
||||
amended_from: DF.Link | None
|
||||
@@ -84,7 +84,7 @@ class SalesOrder(SellingController):
|
||||
commission_rate: DF.Float
|
||||
company: DF.Link
|
||||
company_address: DF.Link | None
|
||||
company_address_display: DF.SmallText | None
|
||||
company_address_display: DF.TextEditor | None
|
||||
contact_display: DF.SmallText | None
|
||||
contact_email: DF.Data | None
|
||||
contact_mobile: DF.SmallText | None
|
||||
@@ -104,7 +104,7 @@ class SalesOrder(SellingController):
|
||||
]
|
||||
disable_rounded_total: DF.Check
|
||||
discount_amount: DF.Currency
|
||||
dispatch_address: DF.SmallText | None
|
||||
dispatch_address: DF.TextEditor | None
|
||||
dispatch_address_name: DF.Link | None
|
||||
from_date: DF.Date | None
|
||||
grand_total: DF.Currency
|
||||
@@ -147,7 +147,7 @@ class SalesOrder(SellingController):
|
||||
select_print_heading: DF.Link | None
|
||||
selling_price_list: DF.Link
|
||||
set_warehouse: DF.Link | None
|
||||
shipping_address: DF.SmallText | None
|
||||
shipping_address: DF.TextEditor | None
|
||||
shipping_address_name: DF.Link | None
|
||||
shipping_rule: DF.Link | None
|
||||
skip_delivery_note: DF.Check
|
||||
@@ -755,6 +755,13 @@ def get_list_context(context=None):
|
||||
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()
|
||||
def close_or_unclose_sales_orders(names, status):
|
||||
if not frappe.has_permission("Sales Order", "write"):
|
||||
|
||||
@@ -68,10 +68,10 @@ frappe.listview_settings["Sales Order"] = {
|
||||
});
|
||||
|
||||
listview.page.add_action_item(__("Delivery Note"), () => {
|
||||
frappe.db
|
||||
.get_single_value("Selling Settings", "enable_cutoff_date_on_bulk_delivery_note_creation")
|
||||
.then((value) => {
|
||||
if (value) {
|
||||
frappe.call({
|
||||
method: "erpnext.selling.doctype.sales_order.sales_order.is_enable_cutoff_date_on_bulk_delivery_note_creation",
|
||||
callback: (r) => {
|
||||
if (r.message) {
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
title: __("Select Items up to Delivery Date"),
|
||||
fields: [
|
||||
@@ -98,7 +98,8 @@ frappe.listview_settings["Sales Order"] = {
|
||||
} else {
|
||||
erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Delivery Note");
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
listview.page.add_action_item(__("Advance Payment"), () => {
|
||||
|
||||
@@ -834,7 +834,8 @@
|
||||
"label": "Purchase Order",
|
||||
"options": "Purchase Order",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_89",
|
||||
@@ -909,7 +910,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-01-25 14:24:00.330219",
|
||||
"modified": "2024-03-21 18:15:56.625005",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order Item",
|
||||
|
||||
@@ -245,6 +245,8 @@ class DeprecatedBatchNoValuation:
|
||||
if 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):
|
||||
self.non_batchwise_balance_value += flt(d.batch_value)
|
||||
self.non_batchwise_balance_qty += flt(d.batch_qty)
|
||||
|
||||
@@ -351,7 +351,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_address",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Shipping Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -408,7 +408,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Billing Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -420,7 +420,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "company_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Company Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -1289,7 +1289,7 @@
|
||||
{
|
||||
"depends_on": "dispatch_address_name",
|
||||
"fieldname": "dispatch_address",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Dispatch Address",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -1397,7 +1397,7 @@
|
||||
"idx": 146,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-20 16:05:02.854990",
|
||||
"modified": "2024-03-22 16:15:07.253135",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note",
|
||||
|
||||
@@ -35,7 +35,7 @@ class DeliveryNote(SellingController):
|
||||
from erpnext.stock.doctype.packed_item.packed_item import PackedItem
|
||||
|
||||
additional_discount_percentage: DF.Float
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amended_from: DF.Link | None
|
||||
amount_eligible_for_commission: DF.Currency
|
||||
apply_discount_on: DF.Literal["", "Grand Total", "Net Total"]
|
||||
@@ -52,7 +52,7 @@ class DeliveryNote(SellingController):
|
||||
commission_rate: DF.Float
|
||||
company: DF.Link
|
||||
company_address: DF.Link | None
|
||||
company_address_display: DF.SmallText | None
|
||||
company_address_display: DF.TextEditor | None
|
||||
contact_display: DF.SmallText | None
|
||||
contact_email: DF.Data | None
|
||||
contact_mobile: DF.SmallText | None
|
||||
@@ -66,7 +66,7 @@ class DeliveryNote(SellingController):
|
||||
customer_name: DF.Data | None
|
||||
disable_rounded_total: DF.Check
|
||||
discount_amount: DF.Currency
|
||||
dispatch_address: DF.SmallText | None
|
||||
dispatch_address: DF.TextEditor | None
|
||||
dispatch_address_name: DF.Link | None
|
||||
driver: DF.Link | None
|
||||
driver_name: DF.Data | None
|
||||
@@ -117,7 +117,7 @@ class DeliveryNote(SellingController):
|
||||
set_posting_time: DF.Check
|
||||
set_target_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_rule: DF.Link | None
|
||||
source: DF.Link | None
|
||||
|
||||
@@ -796,7 +796,8 @@
|
||||
"label": "Purchase Order",
|
||||
"options": "Purchase Order",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_82",
|
||||
@@ -912,7 +913,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-02-04 14:10:31.750340",
|
||||
"modified": "2024-03-21 18:15:07.603672",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note Item",
|
||||
|
||||
@@ -7,7 +7,6 @@ from frappe import _, bold
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder import Criterion
|
||||
from frappe.query_builder.functions import Cast_
|
||||
from frappe.utils import getdate
|
||||
|
||||
|
||||
class ItemPriceDuplicateItem(frappe.ValidationError):
|
||||
@@ -46,7 +45,7 @@ class ItemPrice(Document):
|
||||
|
||||
def validate(self):
|
||||
self.validate_item()
|
||||
self.validate_dates()
|
||||
self.validate_from_to_dates("valid_from", "valid_upto")
|
||||
self.update_price_list_details()
|
||||
self.update_item_details()
|
||||
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))
|
||||
|
||||
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):
|
||||
if self.price_list:
|
||||
price_list_details = frappe.db.get_value(
|
||||
|
||||
@@ -313,7 +313,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -352,7 +352,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Shipping Address",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -1095,7 +1095,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "billing_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Billing Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -1252,7 +1252,7 @@
|
||||
"idx": 261,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-20 16:05:31.713453",
|
||||
"modified": "2024-03-22 16:15:10.937188",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt",
|
||||
|
||||
@@ -40,7 +40,7 @@ class PurchaseReceipt(BuyingController):
|
||||
from erpnext.stock.doctype.purchase_receipt_item.purchase_receipt_item import PurchaseReceiptItem
|
||||
|
||||
additional_discount_percentage: DF.Float
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amended_from: DF.Link | None
|
||||
apply_discount_on: DF.Literal["", "Grand Total", "Net Total"]
|
||||
apply_putaway_rule: DF.Check
|
||||
@@ -56,7 +56,7 @@ class PurchaseReceipt(BuyingController):
|
||||
base_total: DF.Currency
|
||||
base_total_taxes_and_charges: DF.Currency
|
||||
billing_address: DF.Link | None
|
||||
billing_address_display: DF.SmallText | None
|
||||
billing_address_display: DF.TextEditor | None
|
||||
buying_price_list: DF.Link | None
|
||||
company: DF.Link
|
||||
contact_display: DF.SmallText | None
|
||||
@@ -109,7 +109,7 @@ class PurchaseReceipt(BuyingController):
|
||||
set_posting_time: DF.Check
|
||||
set_warehouse: 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
|
||||
status: DF.Literal["", "Draft", "To Bill", "Completed", "Return Issued", "Cancelled", "Closed"]
|
||||
subcontracting_receipt: DF.Link | None
|
||||
|
||||
@@ -1081,7 +1081,9 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle
|
||||
cint(frappe.user_defaults?.use_serial_batch_fields) === 1
|
||||
) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "source_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Source Warehouse Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -336,7 +336,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "target_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Target Warehouse Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -464,7 +464,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Address"
|
||||
},
|
||||
{
|
||||
@@ -681,7 +681,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-01-12 11:56:58.644882",
|
||||
"modified": "2024-03-22 16:23:13.683565",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry",
|
||||
|
||||
@@ -85,7 +85,7 @@ class StockEntry(StockController):
|
||||
|
||||
add_to_transit: DF.Check
|
||||
additional_costs: DF.Table[LandedCostTaxesandCharges]
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amended_from: DF.Link | None
|
||||
apply_putaway_rule: DF.Check
|
||||
bom_no: DF.Link | None
|
||||
@@ -127,14 +127,14 @@ class StockEntry(StockController):
|
||||
scan_barcode: DF.Data | None
|
||||
select_print_heading: DF.Link | None
|
||||
set_posting_time: DF.Check
|
||||
source_address_display: DF.SmallText | None
|
||||
source_address_display: DF.TextEditor | None
|
||||
source_warehouse_address: DF.Link | None
|
||||
stock_entry_type: DF.Link
|
||||
subcontracting_order: DF.Link | None
|
||||
supplier: DF.Link | None
|
||||
supplier_address: DF.Link | None
|
||||
supplier_name: DF.Data | None
|
||||
target_address_display: DF.SmallText | None
|
||||
target_address_display: DF.TextEditor | None
|
||||
target_warehouse_address: DF.Link | None
|
||||
to_warehouse: DF.Link | None
|
||||
total_additional_costs: DF.Currency
|
||||
@@ -2541,6 +2541,7 @@ class StockEntry(StockController):
|
||||
)
|
||||
|
||||
d.serial_and_batch_bundle = id
|
||||
d.use_serial_batch_fields = 0
|
||||
|
||||
def get_available_serial_nos(self) -> List[str]:
|
||||
serial_nos = []
|
||||
@@ -2640,7 +2641,9 @@ def make_stock_in_entry(source_name, target_doc=None):
|
||||
def set_missing_values(source, target):
|
||||
target.stock_entry_type = "Material Transfer"
|
||||
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):
|
||||
target_doc.t_warehouse = ""
|
||||
|
||||
@@ -825,7 +825,9 @@ def get_price_list_rate(args, item_doc, out=None):
|
||||
):
|
||||
if args.price_list and args.rate:
|
||||
insert_item_price(args)
|
||||
return out
|
||||
|
||||
if not price_list_rate:
|
||||
return out
|
||||
|
||||
out.price_list_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
|
||||
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(IfNull(ip.batch_no, ""), order=frappe.qb.desc)
|
||||
.orderby(ip.uom, order=frappe.qb.desc)
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
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"])
|
||||
)
|
||||
|
||||
return query.run()
|
||||
return query.run(as_dict=True)
|
||||
|
||||
|
||||
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)
|
||||
if price_list_rate:
|
||||
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
|
||||
else:
|
||||
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
|
||||
|
||||
if item_price_data:
|
||||
if item_price_data[0][2] == args.get("uom"):
|
||||
return item_price_data[0][1]
|
||||
if item_price_data[0].uom == args.get("uom"):
|
||||
return item_price_data[0].price_list_rate
|
||||
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:
|
||||
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):
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Supplier Address Details",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -230,7 +230,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Shipping Address Details",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -243,7 +243,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "billing_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Billing Address Details",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -454,7 +454,7 @@
|
||||
"icon": "fa fa-file-text",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-01-03 20:56:04.670380",
|
||||
"modified": "2024-03-22 16:15:07.797633",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subcontracting",
|
||||
"name": "Subcontracting Order",
|
||||
|
||||
@@ -36,10 +36,10 @@ class SubcontractingOrder(SubcontractingController):
|
||||
)
|
||||
|
||||
additional_costs: DF.Table[LandedCostTaxesandCharges]
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amended_from: DF.Link | None
|
||||
billing_address: DF.Link | None
|
||||
billing_address_display: DF.SmallText | None
|
||||
billing_address_display: DF.TextEditor | None
|
||||
company: DF.Link
|
||||
contact_display: DF.SmallText | None
|
||||
contact_email: DF.SmallText | None
|
||||
@@ -59,7 +59,7 @@ class SubcontractingOrder(SubcontractingController):
|
||||
set_reserve_warehouse: DF.Link | None
|
||||
set_warehouse: DF.Link | None
|
||||
shipping_address: DF.Link | None
|
||||
shipping_address_display: DF.SmallText | None
|
||||
shipping_address_display: DF.TextEditor | None
|
||||
status: DF.Literal[
|
||||
"Draft",
|
||||
"Open",
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -231,7 +231,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Shipping Address",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -521,7 +521,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "billing_address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Billing Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -645,7 +645,7 @@
|
||||
"in_create": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-11-16 13:04:00.710534",
|
||||
"modified": "2024-03-22 16:15:08.074134",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subcontracting",
|
||||
"name": "Subcontracting Receipt",
|
||||
|
||||
@@ -33,13 +33,13 @@ class SubcontractingReceipt(SubcontractingController):
|
||||
)
|
||||
|
||||
additional_costs: DF.Table[LandedCostTaxesandCharges]
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amended_from: DF.Link | None
|
||||
auto_repeat: DF.Link | None
|
||||
bill_date: DF.Date | None
|
||||
bill_no: DF.Data | None
|
||||
billing_address: DF.Link | None
|
||||
billing_address_display: DF.SmallText | None
|
||||
billing_address_display: DF.TextEditor | None
|
||||
company: DF.Link
|
||||
contact_display: DF.SmallText | None
|
||||
contact_email: DF.SmallText | None
|
||||
@@ -69,7 +69,7 @@ class SubcontractingReceipt(SubcontractingController):
|
||||
set_posting_time: DF.Check
|
||||
set_warehouse: 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"]
|
||||
supplied_items: DF.Table[SubcontractingReceiptSuppliedItem]
|
||||
supplier: DF.Link
|
||||
@@ -149,7 +149,9 @@ class SubcontractingReceipt(SubcontractingController):
|
||||
self.update_prevdoc_status()
|
||||
self.set_subcontracting_order_status()
|
||||
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.make_gl_entries()
|
||||
self.repost_future_sle_and_gle()
|
||||
|
||||
@@ -292,6 +292,7 @@ class TestSubcontractingReceipt(FrappeTestCase):
|
||||
self.assertRaises(OverAllowanceError, make_return_subcontracting_receipt, **args)
|
||||
|
||||
def test_subcontracting_receipt_no_gl_entry(self):
|
||||
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
|
||||
sco = get_subcontracting_order()
|
||||
rm_items = get_rm_items(sco.supplied_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
|
||||
self.assertEqual(stock_value_difference, 2100)
|
||||
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):
|
||||
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
|
||||
sco = get_subcontracting_order(
|
||||
company="_Test Company with perpetual inventory",
|
||||
warehouse="Stores - TCP1",
|
||||
@@ -387,6 +390,7 @@ class TestSubcontractingReceipt(FrappeTestCase):
|
||||
scr.reload()
|
||||
scr.cancel()
|
||||
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):
|
||||
# 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):
|
||||
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
|
||||
set_backflush_based_on("BOM")
|
||||
|
||||
fg_item = make_item(
|
||||
@@ -760,9 +765,11 @@ class TestSubcontractingReceipt(FrappeTestCase):
|
||||
frappe.db.set_single_value(
|
||||
"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):
|
||||
# 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")
|
||||
|
||||
# Step - 2: Create FG and RM Items
|
||||
@@ -820,6 +827,8 @@ class TestSubcontractingReceipt(FrappeTestCase):
|
||||
self.assertEqual(rm_item.rate, 100)
|
||||
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):
|
||||
from erpnext.stock.doctype.quality_inspection.test_quality_inspection import (
|
||||
create_quality_inspection,
|
||||
|
||||
@@ -311,7 +311,7 @@
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -379,7 +379,7 @@
|
||||
"icon": "fa fa-bug",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2023-11-28 17:30:35.676410",
|
||||
"modified": "2024-03-22 16:01:11.412114",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Support",
|
||||
"name": "Warranty Claim",
|
||||
|
||||
@@ -18,7 +18,7 @@ class WarrantyClaim(TransactionBase):
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
address_display: DF.SmallText | None
|
||||
address_display: DF.TextEditor | None
|
||||
amc_expiry_date: DF.Date | None
|
||||
amended_from: DF.Link | None
|
||||
company: DF.Link
|
||||
|
||||
@@ -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.to_doctype = to_doctype
|
||||
transaction_log.retried = restarted
|
||||
transaction_log.save()
|
||||
transaction_log.save(ignore_permissions=True)
|
||||
|
||||
|
||||
def show_job_status(fail_count, deserialized_data_count, to_doctype):
|
||||
|
||||
Reference in New Issue
Block a user