Merge branch 'develop' into version-15-beta

This commit is contained in:
Ankush Menat
2023-07-20 13:20:52 +05:30
55 changed files with 800 additions and 483 deletions

View File

@@ -7,11 +7,9 @@ on:
- '**.css'
- '**.md'
- '**.html'
push:
branches: [ develop ]
paths-ignore:
- '**.js'
- '**.md'
schedule:
# Run everday at midnight UTC / 5:30 IST
- cron: "0 0 * * *"
workflow_dispatch:
inputs:
user:

View File

@@ -4,18 +4,19 @@
"creation": "2020-07-17 11:25:34.593061",
"docstatus": 0,
"doctype": "Dashboard Chart",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"frappe.sys_defaults.fiscal_year\",\"to_fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"erpnext.utils.get_fiscal_year()\",\"to_fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
"filters_json": "{\"period\":\"Monthly\",\"budget_against\":\"Cost Center\",\"show_cumulative\":0}",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"modified": "2020-07-22 12:24:49.144210",
"modified": "2023-07-19 13:13:13.307073",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Budget Variance",
"number_of_groups": 0,
"owner": "Administrator",
"report_name": "Budget Variance Report",
"roles": [],
"timeseries": 0,
"type": "Bar",
"use_report_chart": 1,

View File

@@ -4,18 +4,19 @@
"creation": "2020-07-17 11:25:34.448572",
"docstatus": 0,
"doctype": "Dashboard Chart",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"frappe.sys_defaults.fiscal_year\",\"to_fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"erpnext.utils.get_fiscal_year()\",\"to_fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
"filters_json": "{\"filter_based_on\":\"Fiscal Year\",\"period_start_date\":\"2020-04-01\",\"period_end_date\":\"2021-03-31\",\"periodicity\":\"Yearly\",\"include_default_book_entries\":1}",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"modified": "2020-07-22 12:33:48.888943",
"modified": "2023-07-19 13:08:56.470390",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Profit and Loss",
"number_of_groups": 0,
"owner": "Administrator",
"report_name": "Profit and Loss Statement",
"roles": [],
"timeseries": 0,
"type": "Bar",
"use_report_chart": 1,

View File

@@ -14,10 +14,8 @@ class AccountClosingBalance(Document):
pass
def make_closing_entries(closing_entries, voucher_name):
def make_closing_entries(closing_entries, voucher_name, company, closing_date):
accounting_dimensions = get_accounting_dimensions()
company = closing_entries[0].get("company")
closing_date = closing_entries[0].get("closing_date")
previous_closing_entries = get_previous_closing_entries(
company, closing_date, accounting_dimensions

View File

@@ -20,5 +20,11 @@ frappe.ui.form.on('Accounting Period', {
}
});
}
frm.set_query("document_type", "closed_documents", () => {
return {
query: "erpnext.controllers.queries.get_doctypes_for_closing",
}
});
}
});

View File

@@ -11,6 +11,10 @@ class OverlapError(frappe.ValidationError):
pass
class ClosedAccountingPeriod(frappe.ValidationError):
pass
class AccountingPeriod(Document):
def validate(self):
self.validate_overlap()
@@ -65,3 +69,42 @@ class AccountingPeriod(Document):
"closed_documents",
{"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed},
)
def validate_accounting_period_on_doc_save(doc, method=None):
if doc.doctype == "Bank Clearance":
return
elif doc.doctype == "Asset":
if doc.is_existing_asset:
return
else:
date = doc.available_for_use_date
elif doc.doctype == "Asset Repair":
date = doc.completion_date
else:
date = doc.posting_date
ap = frappe.qb.DocType("Accounting Period")
cd = frappe.qb.DocType("Closed Document")
accounting_period = (
frappe.qb.from_(ap)
.from_(cd)
.select(ap.name)
.where(
(ap.name == cd.parent)
& (ap.company == doc.company)
& (cd.closed == 1)
& (cd.document_type == doc.doctype)
& (date >= ap.start_date)
& (date <= ap.end_date)
)
).run(as_dict=1)
if accounting_period:
frappe.throw(
_("You cannot create a {0} within the closed Accounting Period {1}").format(
doc.doctype, frappe.bold(accounting_period[0]["name"])
),
ClosedAccountingPeriod,
)

View File

@@ -6,9 +6,11 @@ import unittest
import frappe
from frappe.utils import add_months, nowdate
from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError
from erpnext.accounts.doctype.accounting_period.accounting_period import (
ClosedAccountingPeriod,
OverlapError,
)
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.general_ledger import ClosedAccountingPeriod
test_dependencies = ["Item"]
@@ -33,9 +35,9 @@ class TestAccountingPeriod(unittest.TestCase):
ap1.save()
doc = create_sales_invoice(
do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
)
self.assertRaises(ClosedAccountingPeriod, doc.submit)
self.assertRaises(ClosedAccountingPeriod, doc.save)
def tearDown(self):
for d in frappe.get_all("Accounting Period"):

View File

@@ -8,17 +8,6 @@ frappe.ui.form.on('Fiscal Year', {
frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1));
}
},
refresh: function (frm) {
if (!frm.doc.__islocal && (frm.doc.name != frappe.sys_defaults.fiscal_year)) {
frm.add_custom_button(__("Set as Default"), () => frm.events.set_as_default(frm));
frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'"));
} else {
frm.set_intro("");
}
},
set_as_default: function(frm) {
return frm.call('set_as_default');
},
year_start_date: function(frm) {
if (!frm.doc.is_short_year) {
let year_end_date =

View File

@@ -4,28 +4,12 @@
import frappe
from dateutil.relativedelta import relativedelta
from frappe import _, msgprint
from frappe import _
from frappe.model.document import Document
from frappe.utils import add_days, add_years, cstr, getdate
class FiscalYear(Document):
@frappe.whitelist()
def set_as_default(self):
frappe.db.set_single_value("Global Defaults", "current_fiscal_year", self.name)
global_defaults = frappe.get_doc("Global Defaults")
global_defaults.check_permission("write")
global_defaults.on_update()
# clear cache
frappe.clear_cache()
msgprint(
_(
"{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect."
).format(self.name)
)
def validate(self):
self.validate_dates()
self.validate_overlap()
@@ -68,13 +52,6 @@ class FiscalYear(Document):
frappe.cache().delete_value("fiscal_years")
def on_trash(self):
global_defaults = frappe.get_doc("Global Defaults")
if global_defaults.current_fiscal_year == self.name:
frappe.throw(
_(
"You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings"
).format(self.name)
)
frappe.cache().delete_value("fiscal_years")
def validate_overlap(self):

View File

@@ -408,6 +408,15 @@ class JournalEntry(AccountsController):
d.idx, d.account
)
)
elif (
d.party_type
and frappe.db.get_value("Party Type", d.party_type, "account_type") != account_type
):
frappe.throw(
_("Row {0}: Account {1} and Party Type {2} have different account types").format(
d.idx, d.account, d.party_type
)
)
def check_credit_limit(self):
customers = list(

View File

@@ -226,10 +226,12 @@ class PaymentEntry(AccountsController):
latest_lookup = {}
for d in latest_references:
d = frappe._dict(d)
latest_lookup.update({(d.voucher_type, d.voucher_no): d})
latest_lookup.setdefault((d.voucher_type, d.voucher_no), frappe._dict())[d.payment_term] = d
for d in self.get("references"):
latest = latest_lookup.get((d.reference_doctype, d.reference_name))
latest = (latest_lookup.get((d.reference_doctype, d.reference_name)) or frappe._dict()).get(
d.payment_term
)
# The reference has already been fully paid
if not latest:
@@ -251,6 +253,18 @@ class PaymentEntry(AccountsController):
if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount):
frappe.throw(fail_message.format(d.idx))
if d.payment_term and (
(flt(d.allocated_amount)) > 0
and flt(d.allocated_amount) > flt(latest.payment_term_outstanding)
):
frappe.throw(
_(
"Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3}"
).format(
d.idx, d.allocated_amount, latest.payment_term_outstanding, d.payment_term
)
)
# Check for negative outstanding invoices as well
if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount):
frappe.throw(fail_message.format(d.idx))
@@ -1500,7 +1514,9 @@ def get_outstanding_reference_documents(args, validate=False):
accounting_dimensions=accounting_dimensions_filter,
)
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
outstanding_invoices = split_invoices_based_on_payment_terms(
outstanding_invoices, args.get("company")
)
for d in outstanding_invoices:
d["exchange_rate"] = 1
@@ -1560,8 +1576,27 @@ def get_outstanding_reference_documents(args, validate=False):
return data
def split_invoices_based_on_payment_terms(outstanding_invoices):
def split_invoices_based_on_payment_terms(outstanding_invoices, company):
invoice_ref_based_on_payment_terms = {}
company_currency = (
frappe.db.get_value("Company", company, "default_currency") if company else None
)
exc_rates = frappe._dict()
for doctype in ["Sales Invoice", "Purchase Invoice"]:
invoices = [x.voucher_no for x in outstanding_invoices if x.voucher_type == doctype]
for x in frappe.db.get_all(
doctype,
filters={"name": ["in", invoices]},
fields=["name", "currency", "conversion_rate", "party_account_currency"],
):
exc_rates[x.name] = frappe._dict(
conversion_rate=x.conversion_rate,
currency=x.currency,
party_account_currency=x.party_account_currency,
company_currency=company_currency,
)
for idx, d in enumerate(outstanding_invoices):
if d.voucher_type in ["Sales Invoice", "Purchase Invoice"]:
payment_term_template = frappe.db.get_value(
@@ -1578,6 +1613,14 @@ def split_invoices_based_on_payment_terms(outstanding_invoices):
for payment_term in payment_schedule:
if payment_term.outstanding > 0.1:
doc_details = exc_rates.get(payment_term.parent, None)
is_multi_currency_acc = (doc_details.currency != doc_details.company_currency) and (
doc_details.party_account_currency != doc_details.company_currency
)
payment_term_outstanding = flt(payment_term.outstanding)
if not is_multi_currency_acc:
payment_term_outstanding = doc_details.conversion_rate * flt(payment_term.outstanding)
invoice_ref_based_on_payment_terms.setdefault(idx, [])
invoice_ref_based_on_payment_terms[idx].append(
frappe._dict(
@@ -1589,6 +1632,7 @@ def split_invoices_based_on_payment_terms(outstanding_invoices):
"posting_date": d.posting_date,
"invoice_amount": flt(d.invoice_amount),
"outstanding_amount": flt(d.outstanding_amount),
"payment_term_outstanding": payment_term_outstanding,
"payment_amount": payment_term.payment_amount,
"payment_term": payment_term.payment_term,
"account": d.account,
@@ -2371,6 +2415,7 @@ def get_reference_as_per_payment_terms(
"due_date": doc.get("due_date"),
"total_amount": grand_total,
"outstanding_amount": outstanding_amount,
"payment_term_outstanding": payment_term_outstanding,
"payment_term": payment_term.payment_term,
"allocated_amount": payment_term_outstanding,
}

View File

@@ -133,6 +133,8 @@ class PeriodClosingVoucher(AccountsController):
gl_entries=gl_entries,
closing_entries=closing_entries,
voucher_name=self.name,
company=self.company,
closing_date=self.posting_date,
queue="long",
)
frappe.msgprint(
@@ -140,7 +142,7 @@ class PeriodClosingVoucher(AccountsController):
alert=True,
)
else:
process_gl_entries(gl_entries, closing_entries, voucher_name=self.name)
process_gl_entries(gl_entries, closing_entries, self.name, self.company, self.posting_date)
def get_grouped_gl_entries(self, get_opening_entries=False):
closing_entries = []
@@ -321,7 +323,7 @@ class PeriodClosingVoucher(AccountsController):
return query.run(as_dict=1)
def process_gl_entries(gl_entries, closing_entries, voucher_name=None):
def process_gl_entries(gl_entries, closing_entries, voucher_name, company, closing_date):
from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
make_closing_entries,
)
@@ -329,7 +331,7 @@ def process_gl_entries(gl_entries, closing_entries, voucher_name=None):
try:
make_gl_entries(gl_entries, merge_entries=False)
make_closing_entries(gl_entries + closing_entries, voucher_name=voucher_name)
make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date)
frappe.db.set_value(
"Period Closing Voucher", gl_entries[0].get("voucher_no"), "gle_processing_status", "Completed"
)

View File

@@ -13,14 +13,11 @@ import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
from erpnext.accounts.doctype.accounting_period.accounting_period import ClosedAccountingPeriod
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
from erpnext.accounts.utils import create_payment_ledger_entry
class ClosedAccountingPeriod(frappe.ValidationError):
pass
def make_gl_entries(
gl_map,
cancel=False,

View File

@@ -49,7 +49,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("Start Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
on_change: () => {
frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('from_fiscal_year'), function(r) {
@@ -65,7 +65,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("End Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
on_change: () => {
frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('to_fiscal_year'), function(r) {
@@ -139,7 +139,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
return value;
},
onload: function() {
let fiscal_year = frappe.defaults.get_user_default("fiscal_year")
let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today());
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);

View File

@@ -48,7 +48,7 @@ function get_filters() {
"label": __("Start Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1
},
{
@@ -56,7 +56,7 @@ function get_filters() {
"label": __("End Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1
},
{
@@ -100,7 +100,7 @@ frappe.query_reports["Deferred Revenue and Expense"] = {
return default_formatter(value, row, column, data);
},
onload: function(report){
let fiscal_year = frappe.defaults.get_user_default("fiscal_year");
let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today());
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);

View File

@@ -4,9 +4,10 @@
import frappe
from frappe import _, qb
from frappe.query_builder import Column, functions
from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, rounded
from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, getdate, rounded
from erpnext.accounts.report.financial_statements import get_period_list
from erpnext.accounts.utils import get_fiscal_year
class Deferred_Item(object):
@@ -226,7 +227,7 @@ class Deferred_Revenue_and_Expense_Report(object):
# If no filters are provided, get user defaults
if not filters:
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date=getdate()))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),

View File

@@ -10,6 +10,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
from erpnext.accounts.report.deferred_revenue_and_expense.deferred_revenue_and_expense import (
Deferred_Revenue_and_Expense_Report,
)
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
from erpnext.stock.doctype.item.test_item import create_item
@@ -116,7 +117,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
pda.submit()
# execute report
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),
@@ -209,7 +210,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
pda.submit()
# execute report
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),
@@ -297,7 +298,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
pda.submit()
# execute report
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),

View File

@@ -18,7 +18,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
"on_change": function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -25,7 +25,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
"on_change": function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -17,7 +17,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
"on_change": function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -221,7 +221,10 @@ def get_opening_balance(
)
else:
if start_date:
opening_balance = opening_balance.where(closing_balance.posting_date >= start_date)
opening_balance = opening_balance.where(
(closing_balance.posting_date >= start_date)
& (closing_balance.posting_date < filters.from_date)
)
opening_balance = opening_balance.where(closing_balance.is_opening == "No")
else:
opening_balance = opening_balance.where(

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Trial Balance for Party"] = {
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
"on_change": function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -1110,6 +1110,11 @@ def get_autoname_with_number(number_value, doc_title, company):
return " - ".join(parts)
def parse_naming_series_variable(doc, variable):
if variable == "FY":
return get_fiscal_year(date=doc.get("posting_date"), company=doc.get("company"))[0]
@frappe.whitelist()
def get_coa(doctype, parent, is_root, chart=None):
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import (

View File

@@ -82,7 +82,7 @@ frappe.query_reports["Fixed Asset Register"] = {
"label": __("Start Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
},
{
@@ -90,7 +90,7 @@ frappe.query_reports["Fixed Asset Register"] = {
"label": __("End Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
},
{

View File

@@ -5,18 +5,19 @@
"custom_options": "{\"type\": \"line\", \"axisOptions\": {\"shortenYAxisNumbers\": 1}, \"tooltipOptions\": {}, \"lineOptions\": {\"regionFill\": 1}}",
"docstatus": 0,
"doctype": "Dashboard Chart",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
"filters_json": "{\"period\":\"Monthly\",\"period_based_on\":\"posting_date\",\"based_on\":\"Item\"}",
"idx": 0,
"idx": 1,
"is_public": 1,
"is_standard": 1,
"modified": "2020-07-21 16:13:25.092287",
"modified": "2023-07-19 13:06:42.937941",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Trends",
"number_of_groups": 0,
"owner": "Administrator",
"report_name": "Purchase Order Trends",
"roles": [],
"timeseries": 0,
"type": "Line",
"use_report_chart": 1,

View File

@@ -4,18 +4,19 @@
"creation": "2020-07-20 21:01:02.329519",
"docstatus": 0,
"doctype": "Dashboard Chart",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
"filters_json": "{\"period\":\"Monthly\",\"period_based_on\":\"posting_date\",\"based_on\":\"Supplier\"}",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"modified": "2020-07-22 12:43:40.829652",
"modified": "2023-07-19 13:07:41.753556",
"modified_by": "Administrator",
"module": "Buying",
"name": "Top Suppliers",
"number_of_groups": 0,
"owner": "Administrator",
"report_name": "Purchase Receipt Trends",
"roles": [],
"timeseries": 0,
"type": "Bar",
"use_report_chart": 1,

View File

@@ -822,6 +822,15 @@ def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(query, filters)
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_doctypes_for_closing(doctype, txt, searchfield, start, page_len, filters):
doctypes = frappe.get_hooks("period_closing_doctypes")
if txt:
doctypes = [d for d in doctypes if txt.lower() in d.lower()]
return [(d,) for d in set(doctypes)]
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_tax_template(doctype, txt, searchfield, start, page_len, filters):

View File

@@ -83,7 +83,7 @@ update_website_context = [
my_account_context = "erpnext.e_commerce.shopping_cart.utils.update_my_account_context"
webform_list_context = "erpnext.controllers.website_list_for_contact.get_webform_list_context"
calendars = ["Task", "Work Order", "Leave Application", "Sales Order", "Holiday List", "ToDo"]
calendars = ["Task", "Work Order", "Sales Order", "Holiday List", "ToDo"]
website_generators = ["Item Group", "Website Item", "BOM", "Sales Partner"]
@@ -285,10 +285,34 @@ standard_queries = {
"Customer": "erpnext.controllers.queries.customer_query",
}
period_closing_doctypes = [
"Sales Invoice",
"Purchase Invoice",
"Journal Entry",
"Bank Clearance",
"Stock Entry",
"Dunning",
"Invoice Discounting",
"Payment Entry",
"Period Closing Voucher",
"Process Deferred Accounting",
"Asset",
"Asset Capitalization",
"Asset Repair",
"Delivery Note",
"Landed Cost Voucher",
"Purchase Receipt",
"Stock Reconciliation",
"Subcontracting Receipt",
]
doc_events = {
"*": {
"validate": "erpnext.support.doctype.service_level_agreement.service_level_agreement.apply",
},
tuple(period_closing_doctypes): {
"validate": "erpnext.accounts.doctype.accounting_period.accounting_period.validate_accounting_period_on_doc_save",
},
"Stock Entry": {
"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
@@ -354,6 +378,11 @@ doc_events = {
},
}
# function should expect the variable and doc as arguments
naming_series_variables = {
"FY": "erpnext.accounts.utils.parse_naming_series_variable",
}
# On cancel event Payment Entry will be exempted and all linked submittable doctype will get cancelled.
# to maintain data integrity we exempted payment entry. it will un-link when sales invoice get cancelled.
# if payment entry not in auto cancel exempted doctypes it will cancel payment entry.
@@ -459,15 +488,6 @@ advance_payment_doctypes = ["Sales Order", "Purchase Order"]
invoice_doctypes = ["Sales Invoice", "Purchase Invoice"]
period_closing_doctypes = [
"Sales Invoice",
"Purchase Invoice",
"Journal Entry",
"Bank Clearance",
"Asset",
"Stock Entry",
]
bank_reconciliation_doctypes = [
"Payment Entry",
"Journal Entry",

View File

@@ -1539,7 +1539,7 @@ def get_reserved_qty_for_production_plan(item_code, warehouse):
frappe.qb.from_(table)
.inner_join(child)
.on(table.name == child.parent)
.select(Sum(child.required_bom_qty * IfNull(child.conversion_factor, 1.0)))
.select(Sum(child.quantity * IfNull(child.conversion_factor, 1.0)))
.where(
(table.docstatus == 1)
& (child.item_code == item_code)

View File

@@ -933,6 +933,54 @@ class TestProductionPlan(FrappeTestCase):
self.assertEqual(after_qty, before_qty)
def test_resered_qty_for_production_plan_for_material_requests_with_multi_UOM(self):
from erpnext.stock.utils import get_or_make_bin
fg_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name
bom_item = make_item(
properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1", "purchase_uom": "Nos"}
).name
if not frappe.db.exists("UOM Conversion Detail", {"parent": bom_item, "uom": "Nos"}):
doc = frappe.get_doc("Item", bom_item)
doc.append("uoms", {"uom": "Nos", "conversion_factor": 25})
doc.save()
make_bom(item=fg_item, raw_materials=[bom_item], source_warehouse="_Test Warehouse - _TC")
bin_name = get_or_make_bin(bom_item, "_Test Warehouse - _TC")
before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
pln = create_production_plan(
item_code=fg_item, planned_qty=100, ignore_existing_ordered_qty=1, stock_uom="_Test UOM 1"
)
for row in pln.mr_items:
self.assertEqual(row.uom, "Nos")
self.assertEqual(row.quantity, 4)
reserved_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
self.assertEqual(reserved_qty - before_qty, 100.0)
pln.submit_material_request = 1
pln.make_work_order()
for work_order in frappe.get_all(
"Work Order",
fields=["name"],
filters={"production_plan": pln.name},
):
wo_doc = frappe.get_doc("Work Order", work_order.name)
wo_doc.source_warehouse = "_Test Warehouse - _TC"
wo_doc.wip_warehouse = "_Test Warehouse 1 - _TC"
wo_doc.fg_warehouse = "_Test Warehouse - _TC"
wo_doc.submit()
reserved_qty_after_mr = flt(
frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")
)
self.assertEqual(reserved_qty_after_mr, before_qty)
def test_skip_available_qty_for_sub_assembly_items(self):
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom

View File

@@ -17,7 +17,7 @@ frappe.query_reports["Job Card Summary"] = {
label: __("Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.defaults.get_user_default("fiscal_year"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
reqd: 1,
on_change: function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -334,4 +334,5 @@ erpnext.patches.v14_0.cleanup_workspaces
erpnext.patches.v15_0.remove_loan_management_module #2023-07-03
erpnext.patches.v14_0.set_report_in_process_SOA
erpnext.buying.doctype.supplier.patches.migrate_supplier_portal_users
execute:frappe.defaults.clear_default("fiscal_year")
erpnext.patches.v15_0.remove_exotel_integration

View File

@@ -69,7 +69,6 @@ def execute():
entries = gl_entries + closing_entries
if entries:
make_closing_entries(entries, voucher_name=pcv.name)
i += 1
company_wise_order[pcv.company].append(pcv.posting_date)
make_closing_entries(entries, pcv.name, pcv.company, pcv.posting_date)
company_wise_order[pcv.company].append(pcv.posting_date)
i += 1

View File

@@ -1,5 +1,3 @@
from contextlib import suppress
import click
import frappe
from frappe import _
@@ -13,12 +11,14 @@ def execute():
if "exotel_integration" in frappe.get_installed_apps():
return
with suppress(Exception):
try:
exotel = frappe.get_doc(SETTINGS_DOCTYPE)
if exotel.enabled:
notify_existing_users()
frappe.delete_doc("DocType", SETTINGS_DOCTYPE)
except Exception:
frappe.log_error("Failed to remove Exotel Integration.")
def notify_existing_users():

View File

@@ -358,12 +358,14 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
}
refresh() {
erpnext.toggle_naming_series();
erpnext.hide_company();
this.set_dynamic_labels();
this.setup_sms();
this.setup_quality_inspection();
this.validate_has_items();
erpnext.utils.view_serial_batch_nos(this.frm);
}
scan_barcode() {

View File

@@ -56,7 +56,7 @@ erpnext.financial_statements = {
// dropdown for links to other financial statements
erpnext.financial_statements.filters = get_filters()
let fiscal_year = frappe.defaults.get_user_default("fiscal_year")
let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today());
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
@@ -137,7 +137,7 @@ function get_filters() {
"label": __("Start Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
},
@@ -146,7 +146,7 @@ function get_filters() {
"label": __("End Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
},

View File

@@ -113,6 +113,23 @@ $.extend(erpnext.utils, {
}
},
view_serial_batch_nos: function(frm) {
let bundle_ids = frm.doc.items.filter(d => d.serial_and_batch_bundle);
if (bundle_ids?.length) {
frm.add_custom_button(__('Serial / Batch Nos'), () => {
frappe.route_options = {
"voucher_no": frm.doc.name,
"voucher_type": frm.doc.doctype,
"from_date": frm.doc.posting_date || frm.doc.transaction_date,
"to_date": frm.doc.posting_date || frm.doc.transaction_date,
"company": frm.doc.company,
};
frappe.set_route("query-report", "Serial and Batch Summary");
}, __('View'));
}
},
add_indicator_for_multicompany: function(frm, info) {
frm.dashboard.stats_area.show();
frm.dashboard.stats_area_row.addClass('flex');
@@ -381,6 +398,27 @@ $.extend(erpnext.utils, {
});
});
});
},
get_fiscal_year: function(date) {
if(!date) {
date = frappe.datetime.get_today();
}
let fiscal_year = '';
frappe.call({
method: "erpnext.accounts.utils.get_fiscal_year",
args: {
date: date
},
async: false,
callback: function(r) {
if (r.message) {
fiscal_year = r.message[0];
}
}
});
return fiscal_year;
}
});
@@ -1003,4 +1041,4 @@ function attach_selector_button(inner_text, append_loction, context, grid_row) {
$btn.on("click", function() {
context.show_serial_batch_selector(grid_row.frm, grid_row.doc, "", "", true);
});
}
}

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Fichier des Ecritures Comptables [FEC]"] = {
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1
}
],

View File

@@ -17,7 +17,7 @@ frappe.query_reports["IRS 1099"] = {
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
"width": 80,
},

View File

@@ -5,18 +5,19 @@
"custom_options": "{\"type\": \"line\", \"axisOptions\": {\"shortenYAxisNumbers\": 1}, \"tooltipOptions\": {}, \"lineOptions\": {\"regionFill\": 1}}",
"docstatus": 0,
"doctype": "Dashboard Chart",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
"filters_json": "{\"period\":\"Monthly\",\"based_on\":\"Item\"}",
"idx": 0,
"idx": 1,
"is_public": 1,
"is_standard": 1,
"modified": "2020-07-22 16:24:45.726270",
"modified": "2023-07-19 13:09:45.341791",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Trends",
"number_of_groups": 0,
"owner": "Administrator",
"report_name": "Sales Order Trends",
"roles": [],
"timeseries": 0,
"type": "Line",
"use_report_chart": 1,

View File

@@ -5,18 +5,19 @@
"custom_options": "",
"docstatus": 0,
"doctype": "Dashboard Chart",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
"filters_json": "{\"period\":\"Yearly\",\"based_on\":\"Customer\"}",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"modified": "2020-07-22 17:03:10.320147",
"modified": "2023-07-19 13:14:20.151502",
"modified_by": "Administrator",
"module": "Selling",
"name": "Top Customers",
"number_of_groups": 0,
"owner": "Administrator",
"report_name": "Delivery Note Trends",
"roles": [],
"timeseries": 0,
"type": "Bar",
"use_report_chart": 1,

View File

@@ -1904,12 +1904,11 @@ class TestSalesOrder(FrappeTestCase):
"voucher_no": so.name,
"voucher_detail_no": item.name,
},
fields=["status", "reserved_qty", "delivered_qty"],
fields=["reserved_qty", "delivered_qty"],
)
for sre_detail in sre_details:
self.assertEqual(sre_detail.reserved_qty, sre_detail.delivered_qty)
self.assertEqual(sre_detail.status, "Delivered")
def test_delivered_item_material_request(self):
"SO -> MR (Manufacture) -> WO. Test if WO Qty is updated in SO."

View File

@@ -1,352 +1,99 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2013-05-02 17:53:24",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"editable_grid": 0,
"actions": [],
"allow_copy": 1,
"creation": "2013-05-02 17:53:24",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"default_company",
"country",
"default_distance_unit",
"column_break_8",
"default_currency",
"hide_currency_symbol",
"disable_rounded_total",
"disable_in_words"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "default_company",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Default Company",
"options": "Company"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "current_fiscal_year",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Current Fiscal Year",
"length": 0,
"no_copy": 0,
"options": "Fiscal Year",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "country",
"fieldtype": "Link",
"label": "Country",
"options": "Country"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "country",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Country",
"length": 0,
"no_copy": 0,
"options": "Country",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "default_distance_unit",
"fieldtype": "Link",
"label": "Default Distance Unit",
"options": "UOM"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "default_distance_unit",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Distance Unit",
"length": 0,
"no_copy": 0,
"options": "UOM",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "column_break_8",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_8",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "INR",
"fieldname": "default_currency",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Default Currency",
"options": "Currency",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "INR",
"fieldname": "default_currency",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Default Currency",
"length": 0,
"no_copy": 0,
"options": "Currency",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"description": "Do not show any symbol like $ etc next to currencies.",
"fieldname": "hide_currency_symbol",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Hide Currency Symbol",
"options": "\nNo\nYes"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Do not show any symbol like $ etc next to currencies.",
"fieldname": "hide_currency_symbol",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Hide Currency Symbol",
"length": 0,
"no_copy": 0,
"options": "\nNo\nYes",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "0",
"description": "If disable, 'Rounded Total' field will not be visible in any transaction",
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Disable Rounded Total"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "If disable, 'Rounded Total' field will not be visible in any transaction",
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Disable Rounded Total",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "If disable, 'In Words' field will not be visible in any transaction",
"fieldname": "disable_in_words",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Disable In Words",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"default": "0",
"description": "If disable, 'In Words' field will not be visible in any transaction",
"fieldname": "disable_in_words",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Disable In Words"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-cog",
"idx": 1,
"image_view": 0,
"in_create": 1,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-10-15 03:08:19.886212",
"modified_by": "Administrator",
"module": "Setup",
"name": "Global Defaults",
"owner": "Administrator",
],
"icon": "fa fa-cog",
"idx": 1,
"in_create": 1,
"issingle": 1,
"links": [],
"modified": "2023-07-01 19:45:00.323953",
"modified_by": "Administrator",
"module": "Setup",
"name": "Global Defaults",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"read": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 1,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
],
"read_only": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}

View File

@@ -10,7 +10,6 @@ from frappe.utils import cint
keydict = {
# "key in defaults": "key in Global Defaults"
"fiscal_year": "current_fiscal_year",
"company": "default_company",
"currency": "default_currency",
"country": "country",
@@ -29,22 +28,6 @@ class GlobalDefaults(Document):
for key in keydict:
frappe.db.set_default(key, self.get(keydict[key], ""))
# update year start date and year end date from fiscal_year
if self.current_fiscal_year:
if fiscal_year := frappe.get_all(
"Fiscal Year",
filters={"name": self.current_fiscal_year},
fields=["year_start_date", "year_end_date"],
limit=1,
order_by=None,
):
ysd = fiscal_year[0].year_start_date or ""
yed = fiscal_year[0].year_end_date or ""
if ysd and yed:
frappe.db.set_default("year_start_date", ysd.strftime("%Y-%m-%d"))
frappe.db.set_default("year_end_date", yed.strftime("%Y-%m-%d"))
# enable default currency
if self.default_currency:
frappe.db.set_value("Currency", self.default_currency, "enabled", 1)

View File

@@ -462,11 +462,9 @@ def install_defaults(args=None): # nosemgrep
def set_global_defaults(args):
global_defaults = frappe.get_doc("Global Defaults", "Global Defaults")
current_fiscal_year = frappe.get_all("Fiscal Year")[0]
global_defaults.update(
{
"current_fiscal_year": current_fiscal_year.name,
"default_currency": args.get("currency"),
"default_company": args.get("company_name"),
"country": args.get("country"),

View File

@@ -118,8 +118,8 @@ class MaterialRequest(BuyingController):
self.title = _("{0} Request for {1}").format(_(self.material_request_type), items)[:100]
def on_submit(self):
self.update_requested_qty()
self.update_requested_qty_in_production_plan()
self.update_requested_qty()
if self.material_request_type == "Purchase":
self.validate_budget()
@@ -178,8 +178,8 @@ class MaterialRequest(BuyingController):
)
def on_cancel(self):
self.update_requested_qty()
self.update_requested_qty_in_production_plan()
self.update_requested_qty()
def get_mr_items_ordered_qty(self, mr_items):
mr_items_ordered_qty = {}
@@ -270,7 +270,13 @@ class MaterialRequest(BuyingController):
item_wh_list.append([d.item_code, d.warehouse])
for item_code, warehouse in item_wh_list:
update_bin_qty(item_code, warehouse, {"indented_qty": get_indented_qty(item_code, warehouse)})
update_bin_qty(
item_code,
warehouse,
{
"indented_qty": get_indented_qty(item_code, warehouse),
},
)
def update_requested_qty_in_production_plan(self):
production_plans = []

View File

@@ -193,7 +193,7 @@
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Naming Series",
"options": "SBB-.####"
"options": "SABB-.########"
},
{
"default": "0",
@@ -244,7 +244,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-04-10 20:02:42.964309",
"modified": "2023-07-16 10:53:04.045605",
"modified_by": "Administrator",
"module": "Stock",
"name": "Serial and Batch Bundle",

View File

@@ -889,13 +889,16 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
@frappe.whitelist()
def get_serial_batch_ledgers(item_code, docstatus=None, voucher_no=None, name=None):
filters = get_filters_for_bundle(item_code, docstatus=docstatus, voucher_no=voucher_no, name=name)
def get_serial_batch_ledgers(item_code=None, docstatus=None, voucher_no=None, name=None):
filters = get_filters_for_bundle(
item_code=item_code, docstatus=docstatus, voucher_no=voucher_no, name=name
)
return frappe.get_all(
"Serial and Batch Bundle",
fields=[
"`tabSerial and Batch Bundle`.`name`",
"`tabSerial and Batch Bundle`.`item_code`",
"`tabSerial and Batch Entry`.`qty`",
"`tabSerial and Batch Entry`.`warehouse`",
"`tabSerial and Batch Entry`.`batch_no`",
@@ -906,12 +909,14 @@ def get_serial_batch_ledgers(item_code, docstatus=None, voucher_no=None, name=No
)
def get_filters_for_bundle(item_code, docstatus=None, voucher_no=None, name=None):
def get_filters_for_bundle(item_code=None, docstatus=None, voucher_no=None, name=None):
filters = [
["Serial and Batch Bundle", "item_code", "=", item_code],
["Serial and Batch Bundle", "is_cancelled", "=", 0],
]
if item_code:
filters.append(["Serial and Batch Bundle", "item_code", "=", item_code])
if not docstatus:
docstatus = [0, 1]

View File

@@ -925,6 +925,7 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle
this.toggle_related_fields(this.frm.doc);
this.toggle_enable_bom();
this.show_stock_ledger();
erpnext.utils.view_serial_batch_nos(this.frm);
if (this.frm.doc.docstatus===1 && erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
this.show_general_ledger();
}

View File

@@ -337,6 +337,7 @@ erpnext.stock.StockReconciliation = class StockReconciliation extends erpnext.st
refresh() {
if(this.frm.doc.docstatus > 0) {
this.show_stock_ledger();
erpnext.utils.view_serial_batch_nos(this.frm);
if (erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
this.show_general_ledger();
}

View File

@@ -0,0 +1,95 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Serial and Batch Summary"] = {
"filters": [
{
"fieldname":"company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("Company"),
},
{
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.get_today()
},
{
"fieldname":"item_code",
"label": __("Item"),
"fieldtype": "Link",
"options": "Item",
},
{
"fieldname":"warehouse",
"label": __("Warehouse"),
"fieldtype": "Link",
"options": "Warehouse",
},
{
"fieldname":"voucher_type",
"label": __("Voucher Type"),
"fieldtype": "Link",
"options": "DocType",
get_query: function() {
return {
query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_voucher_type",
}
}
},
{
"fieldname":"voucher_no",
"label": __("Voucher No"),
"fieldtype": "MultiSelectList",
get_data: function(txt) {
if (!frappe.query_report.filters) return;
let voucher_type = frappe.query_report.get_filter_value('voucher_type');
if (!voucher_type) return;
return frappe.db.get_link_options(voucher_type, txt);
},
},
{
"fieldname":"serial_no",
"label": __("Serial No"),
"fieldtype": "Link",
"options": "Serial No",
get_query: function() {
return {
query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_serial_nos",
filters: {
"item_code": frappe.query_report.get_filter_value('item_code'),
"voucher_type": frappe.query_report.get_filter_value('voucher_type'),
"voucher_no": frappe.query_report.get_filter_value('voucher_no'),
}
}
}
},
{
"fieldname":"batch_no",
"label": __("Batch No"),
"fieldtype": "Link",
"options": "Batch",
get_query: function() {
return {
query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_batch_nos",
filters: {
"item_code": frappe.query_report.get_filter_value('item_code'),
"voucher_type": frappe.query_report.get_filter_value('voucher_type'),
"voucher_no": frappe.query_report.get_filter_value('voucher_no'),
}
}
}
}
]
};

View File

@@ -0,0 +1,38 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2023-07-13 16:53:27.735091",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"json": "{}",
"modified": "2023-07-13 16:53:33.204591",
"modified_by": "Administrator",
"module": "Stock",
"name": "Serial and Batch Summary",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Serial and Batch Bundle",
"report_name": "Serial and Batch Summary",
"report_type": "Script Report",
"roles": [
{
"role": "System Manager"
},
{
"role": "Sales User"
},
{
"role": "Purchase User"
},
{
"role": "Stock User"
},
{
"role": "Maintenance User"
}
]
}

View File

@@ -0,0 +1,245 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
def execute(filters=None):
data = get_data(filters)
columns = get_columns(filters, data)
return columns, data
def get_data(filters):
filter_conditions = get_filter_conditions(filters)
return frappe.get_all(
"Serial and Batch Bundle",
fields=[
"`tabSerial and Batch Bundle`.`voucher_type`",
"`tabSerial and Batch Bundle`.`posting_date`",
"`tabSerial and Batch Bundle`.`name`",
"`tabSerial and Batch Bundle`.`company`",
"`tabSerial and Batch Bundle`.`voucher_no`",
"`tabSerial and Batch Bundle`.`item_code`",
"`tabSerial and Batch Bundle`.`item_name`",
"`tabSerial and Batch Entry`.`serial_no`",
"`tabSerial and Batch Entry`.`batch_no`",
"`tabSerial and Batch Entry`.`warehouse`",
"`tabSerial and Batch Entry`.`incoming_rate`",
"`tabSerial and Batch Entry`.`stock_value_difference`",
"`tabSerial and Batch Entry`.`qty`",
],
filters=filter_conditions,
order_by="posting_date",
)
def get_filter_conditions(filters):
filter_conditions = [
["Serial and Batch Bundle", "docstatus", "=", 1],
["Serial and Batch Bundle", "is_cancelled", "=", 0],
]
for field in ["voucher_type", "voucher_no", "item_code", "warehouse", "company"]:
if filters.get(field):
if field == "voucher_no":
filter_conditions.append(["Serial and Batch Bundle", field, "in", filters.get(field)])
else:
filter_conditions.append(["Serial and Batch Bundle", field, "=", filters.get(field)])
if filters.get("from_date") and filters.get("to_date"):
filter_conditions.append(
[
"Serial and Batch Bundle",
"posting_date",
"between",
[filters.get("from_date"), filters.get("to_date")],
]
)
for field in ["serial_no", "batch_no"]:
if filters.get(field):
filter_conditions.append(["Serial and Batch Entry", field, "=", filters.get(field)])
return filter_conditions
def get_columns(filters, data):
columns = [
{
"label": _("Company"),
"fieldname": "company",
"fieldtype": "Link",
"options": "Company",
"width": 120,
},
{
"label": _("Serial and Batch Bundle"),
"fieldname": "name",
"fieldtype": "Link",
"options": "Serial and Batch Bundle",
"width": 110,
},
{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
]
item_details = {}
item_codes = []
if filters.get("voucher_type"):
item_codes = [d.item_code for d in data]
if filters.get("item_code") or (item_codes and len(list(set(item_codes))) == 1):
item_details = frappe.get_cached_value(
"Item",
filters.get("item_code") or item_codes[0],
["has_serial_no", "has_batch_no"],
as_dict=True,
)
if not filters.get("voucher_no"):
columns.extend(
[
{
"label": _("Voucher Type"),
"fieldname": "voucher_type",
"fieldtype": "Link",
"options": "DocType",
"width": 120,
},
{
"label": _("Voucher No"),
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"options": "voucher_type",
"width": 160,
},
]
)
if not filters.get("item_code"):
columns.extend(
[
{
"label": _("Item Code"),
"fieldname": "item_code",
"fieldtype": "Link",
"options": "Item",
"width": 120,
},
{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 120},
]
)
if not filters.get("warehouse"):
columns.append(
{
"label": _("Warehouse"),
"fieldname": "warehouse",
"fieldtype": "Link",
"options": "Warehouse",
"width": 120,
}
)
if not item_details or item_details.get("has_serial_no"):
columns.append(
{"label": _("Serial No"), "fieldname": "serial_no", "fieldtype": "Data", "width": 120}
)
if not item_details or item_details.get("has_batch_no"):
columns.extend(
[
{"label": _("Batch No"), "fieldname": "batch_no", "fieldtype": "Data", "width": 120},
{"label": _("Batch Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 120},
]
)
columns.extend(
[
{"label": _("Incoming Rate"), "fieldname": "incoming_rate", "fieldtype": "Float", "width": 120},
{
"label": _("Change in Stock Value"),
"fieldname": "stock_value_difference",
"fieldtype": "Float",
"width": 120,
},
]
)
return columns
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_voucher_type(doctype, txt, searchfield, start, page_len, filters):
child_doctypes = frappe.get_all(
"DocField",
filters={"fieldname": "serial_and_batch_bundle"},
fields=["distinct parent as parent"],
)
query_filters = {"options": ["in", [d.parent for d in child_doctypes]]}
if txt:
query_filters["parent"] = ["like", "%{}%".format(txt)]
return frappe.get_all("DocField", filters=query_filters, fields=["distinct parent"], as_list=True)
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_serial_nos(doctype, txt, searchfield, start, page_len, filters):
query_filters = {}
if txt:
query_filters["serial_no"] = ["like", f"%{txt}%"]
if filters.get("voucher_no"):
serial_batch_bundle = frappe.get_cached_value(
"Serial and Batch Bundle",
{"voucher_no": ("in", filters.get("voucher_no")), "docstatus": 1, "is_cancelled": 0},
"name",
)
query_filters["parent"] = serial_batch_bundle
if not txt:
query_filters["serial_no"] = ("is", "set")
return frappe.get_all(
"Serial and Batch Entry", filters=query_filters, fields=["serial_no"], as_list=True
)
else:
query_filters["item_code"] = filters.get("item_code")
return frappe.get_all("Serial No", filters=query_filters, as_list=True)
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_batch_nos(doctype, txt, searchfield, start, page_len, filters):
query_filters = {}
if txt:
query_filters["batch_no"] = ["like", f"%{txt}%"]
if filters.get("voucher_no"):
serial_batch_bundle = frappe.get_cached_value(
"Serial and Batch Bundle",
{"voucher_no": ("in", filters.get("voucher_no")), "docstatus": 1, "is_cancelled": 0},
"name",
)
query_filters["parent"] = serial_batch_bundle
if not txt:
query_filters["batch_no"] = ("is", "set")
return frappe.get_all(
"Serial and Batch Entry", filters=query_filters, fields=["batch_no"], as_list=True
)
else:
query_filters["item"] = filters.get("item_code")
return frappe.get_all("Batch", filters=query_filters, as_list=True)

View File

@@ -15,7 +15,7 @@
{% for item, taxes in itemised_tax.items() %}
<tr>
<td>{{ item }}</td>
<td class='text-right'>
<td class="text-right">
{% if doc.get('is_return') %}
{{ frappe.utils.fmt_money((itemised_taxable_amount.get(item, 0))|abs, None, doc.currency) }}
{% else %}
@@ -25,7 +25,7 @@
{% for tax_account in tax_accounts %}
{% set tax_details = taxes.get(tax_account) %}
{% if tax_details %}
<td class='text-right'>
<td class="text-right">
{% if tax_details.tax_rate or not tax_details.tax_amount %}
({{ tax_details.tax_rate }}%)
{% endif %}