mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-08 15:42:52 +00:00
22
.github/workflows/initiate_release.yml
vendored
22
.github/workflows/initiate_release.yml
vendored
@@ -9,7 +9,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
stable-release:
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
@@ -30,3 +30,23 @@ jobs:
|
|||||||
head: version-${{ matrix.version }}-hotfix
|
head: version-${{ matrix.version }}-hotfix
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
beta-release:
|
||||||
|
name: Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: octokit/request-action@v2.x
|
||||||
|
with:
|
||||||
|
route: POST /repos/{owner}/{repo}/pulls
|
||||||
|
owner: frappe
|
||||||
|
repo: erpnext
|
||||||
|
title: |-
|
||||||
|
"chore: release v15 beta"
|
||||||
|
body: "Automated beta release."
|
||||||
|
base: version-15-beta
|
||||||
|
head: develop
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ frappe.ui.form.on('Bank', {
|
|||||||
},
|
},
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
add_fields_to_mapping_table(frm);
|
add_fields_to_mapping_table(frm);
|
||||||
|
|
||||||
frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank' };
|
|
||||||
|
|
||||||
frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
|
frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
|
||||||
|
|
||||||
if (frm.doc.__islocal) {
|
if (frm.doc.__islocal) {
|
||||||
|
|||||||
@@ -93,6 +93,12 @@ class ExchangeRateRevaluation(Document):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def fetch_and_calculate_accounts_data(self):
|
||||||
|
accounts = self.get_accounts_data()
|
||||||
|
if accounts:
|
||||||
|
for acc in accounts:
|
||||||
|
self.append("accounts", acc)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_accounts_data(self):
|
def get_accounts_data(self):
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
@@ -252,8 +258,8 @@ class ExchangeRateRevaluation(Document):
|
|||||||
new_balance_in_base_currency = 0
|
new_balance_in_base_currency = 0
|
||||||
new_balance_in_account_currency = 0
|
new_balance_in_account_currency = 0
|
||||||
|
|
||||||
current_exchange_rate = calculate_exchange_rate_using_last_gle(
|
current_exchange_rate = (
|
||||||
company, d.account, d.party_type, d.party
|
calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party) or 0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
gain_loss = new_balance_in_account_currency - (
|
gain_loss = new_balance_in_account_currency - (
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"in_filter": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"options": "Company",
|
"options": "Company",
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-01-18 21:11:23.105589",
|
"modified": "2023-07-09 18:11:23.105589",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Item Tax Template",
|
"name": "Item Tax Template",
|
||||||
|
|||||||
@@ -237,10 +237,9 @@ class PaymentEntry(AccountsController):
|
|||||||
_("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name)
|
_("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name)
|
||||||
)
|
)
|
||||||
# The reference has already been partly paid
|
# The reference has already been partly paid
|
||||||
elif (
|
elif latest.outstanding_amount < latest.invoice_amount and flt(
|
||||||
latest.outstanding_amount < latest.invoice_amount
|
d.outstanding_amount, d.precision("outstanding_amount")
|
||||||
and flt(d.outstanding_amount, d.precision("outstanding_amount")) != latest.outstanding_amount
|
) != flt(latest.outstanding_amount, d.precision("outstanding_amount")):
|
||||||
):
|
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
"{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts."
|
"{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts."
|
||||||
@@ -1433,6 +1432,9 @@ def get_outstanding_reference_documents(args, validate=False):
|
|||||||
if args.get("party_type") == "Member":
|
if args.get("party_type") == "Member":
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not args.get("get_outstanding_invoices") and not args.get("get_orders_to_be_billed"):
|
||||||
|
args["get_outstanding_invoices"] = True
|
||||||
|
|
||||||
ple = qb.DocType("Payment Ledger Entry")
|
ple = qb.DocType("Payment Ledger Entry")
|
||||||
common_filter = []
|
common_filter = []
|
||||||
accounting_dimensions_filter = []
|
accounting_dimensions_filter = []
|
||||||
@@ -1627,60 +1629,59 @@ def get_orders_to_be_billed(
|
|||||||
cost_center=None,
|
cost_center=None,
|
||||||
filters=None,
|
filters=None,
|
||||||
):
|
):
|
||||||
|
voucher_type = None
|
||||||
if party_type == "Customer":
|
if party_type == "Customer":
|
||||||
voucher_type = "Sales Order"
|
voucher_type = "Sales Order"
|
||||||
elif party_type == "Supplier":
|
elif party_type == "Supplier":
|
||||||
voucher_type = "Purchase Order"
|
voucher_type = "Purchase Order"
|
||||||
elif party_type == "Employee":
|
|
||||||
voucher_type = None
|
if not voucher_type:
|
||||||
|
return []
|
||||||
|
|
||||||
# Add cost center condition
|
# Add cost center condition
|
||||||
if voucher_type:
|
doc = frappe.get_doc({"doctype": voucher_type})
|
||||||
doc = frappe.get_doc({"doctype": voucher_type})
|
condition = ""
|
||||||
condition = ""
|
if doc and hasattr(doc, "cost_center") and doc.cost_center:
|
||||||
if doc and hasattr(doc, "cost_center") and doc.cost_center:
|
condition = " and cost_center='%s'" % cost_center
|
||||||
condition = " and cost_center='%s'" % cost_center
|
|
||||||
|
|
||||||
orders = []
|
if party_account_currency == company_currency:
|
||||||
if voucher_type:
|
grand_total_field = "base_grand_total"
|
||||||
if party_account_currency == company_currency:
|
rounded_total_field = "base_rounded_total"
|
||||||
grand_total_field = "base_grand_total"
|
else:
|
||||||
rounded_total_field = "base_rounded_total"
|
grand_total_field = "grand_total"
|
||||||
else:
|
rounded_total_field = "rounded_total"
|
||||||
grand_total_field = "grand_total"
|
|
||||||
rounded_total_field = "rounded_total"
|
|
||||||
|
|
||||||
orders = frappe.db.sql(
|
orders = frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
select
|
select
|
||||||
name as voucher_no,
|
name as voucher_no,
|
||||||
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
|
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
|
||||||
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
|
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
|
||||||
transaction_date as posting_date
|
transaction_date as posting_date
|
||||||
from
|
from
|
||||||
`tab{voucher_type}`
|
`tab{voucher_type}`
|
||||||
where
|
where
|
||||||
{party_type} = %s
|
{party_type} = %s
|
||||||
and docstatus = 1
|
and docstatus = 1
|
||||||
and company = %s
|
and company = %s
|
||||||
and ifnull(status, "") != "Closed"
|
and ifnull(status, "") != "Closed"
|
||||||
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
|
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
|
||||||
and abs(100 - per_billed) > 0.01
|
and abs(100 - per_billed) > 0.01
|
||||||
{condition}
|
{condition}
|
||||||
order by
|
order by
|
||||||
transaction_date, name
|
transaction_date, name
|
||||||
""".format(
|
""".format(
|
||||||
**{
|
**{
|
||||||
"rounded_total_field": rounded_total_field,
|
"rounded_total_field": rounded_total_field,
|
||||||
"grand_total_field": grand_total_field,
|
"grand_total_field": grand_total_field,
|
||||||
"voucher_type": voucher_type,
|
"voucher_type": voucher_type,
|
||||||
"party_type": scrub(party_type),
|
"party_type": scrub(party_type),
|
||||||
"condition": condition,
|
"condition": condition,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
(party, company),
|
(party, company),
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
order_list = []
|
order_list = []
|
||||||
for d in orders:
|
for d in orders:
|
||||||
@@ -1713,6 +1714,8 @@ def get_negative_outstanding_invoices(
|
|||||||
cost_center=None,
|
cost_center=None,
|
||||||
condition=None,
|
condition=None,
|
||||||
):
|
):
|
||||||
|
if party_type not in ["Customer", "Supplier"]:
|
||||||
|
return []
|
||||||
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
|
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
|
||||||
account = "debit_to" if voucher_type == "Sales Invoice" else "credit_to"
|
account = "debit_to" if voucher_type == "Sales Invoice" else "credit_to"
|
||||||
supplier_condition = ""
|
supplier_condition = ""
|
||||||
|
|||||||
@@ -549,6 +549,7 @@
|
|||||||
"depends_on": "update_stock",
|
"depends_on": "update_stock",
|
||||||
"fieldname": "rejected_warehouse",
|
"fieldname": "rejected_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
"label": "Rejected Warehouse",
|
"label": "Rejected Warehouse",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Warehouse",
|
"options": "Warehouse",
|
||||||
@@ -1576,7 +1577,7 @@
|
|||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-06-03 16:21:54.637245",
|
"modified": "2023-07-04 17:22:59.145031",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
|||||||
@@ -423,6 +423,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "rejected_warehouse",
|
"fieldname": "rejected_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
"label": "Rejected Warehouse",
|
"label": "Rejected Warehouse",
|
||||||
"options": "Warehouse"
|
"options": "Warehouse"
|
||||||
},
|
},
|
||||||
@@ -904,7 +905,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-07-02 18:39:41.495723",
|
"modified": "2023-07-04 17:22:21.501152",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice Item",
|
"name": "Purchase Invoice Item",
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Shareholder', {
|
frappe.ui.form.on('Shareholder', {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Shareholder' };
|
|
||||||
|
|
||||||
frm.toggle_display(['contact_html'], !frm.doc.__islocal);
|
frm.toggle_display(['contact_html'], !frm.doc.__islocal);
|
||||||
|
|
||||||
if (frm.doc.__islocal) {
|
if (frm.doc.__islocal) {
|
||||||
|
|||||||
@@ -850,7 +850,7 @@ def get_held_invoices(party_type, party):
|
|||||||
|
|
||||||
if party_type == "Supplier":
|
if party_type == "Supplier":
|
||||||
held_invoices = frappe.db.sql(
|
held_invoices = frappe.db.sql(
|
||||||
"select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()",
|
"select name from `tabPurchase Invoice` where on_hold = 1 and release_date IS NOT NULL and release_date > CURDATE()",
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
held_invoices = set(d["name"] for d in held_invoices)
|
held_invoices = set(d["name"] for d in held_invoices)
|
||||||
@@ -1408,6 +1408,50 @@ def check_and_delete_linked_reports(report):
|
|||||||
frappe.delete_doc("Desktop Icon", icon)
|
frappe.delete_doc("Desktop Icon", icon)
|
||||||
|
|
||||||
|
|
||||||
|
def create_err_and_its_journals(companies: list = None) -> None:
|
||||||
|
if companies:
|
||||||
|
for company in companies:
|
||||||
|
err = frappe.new_doc("Exchange Rate Revaluation")
|
||||||
|
err.company = company.name
|
||||||
|
err.posting_date = nowdate()
|
||||||
|
err.rounding_loss_allowance = 0.0
|
||||||
|
|
||||||
|
err.fetch_and_calculate_accounts_data()
|
||||||
|
if err.accounts:
|
||||||
|
err.save().submit()
|
||||||
|
response = err.make_jv_entries()
|
||||||
|
|
||||||
|
if company.submit_err_jv:
|
||||||
|
jv = response.get("revaluation_jv", None)
|
||||||
|
jv and frappe.get_doc("Journal Entry", jv).submit()
|
||||||
|
jv = response.get("zero_balance_jv", None)
|
||||||
|
jv and frappe.get_doc("Journal Entry", jv).submit()
|
||||||
|
|
||||||
|
|
||||||
|
def auto_create_exchange_rate_revaluation_daily() -> None:
|
||||||
|
"""
|
||||||
|
Executed by background job
|
||||||
|
"""
|
||||||
|
companies = frappe.db.get_all(
|
||||||
|
"Company",
|
||||||
|
filters={"auto_exchange_rate_revaluation": 1, "auto_err_frequency": "Daily"},
|
||||||
|
fields=["name", "submit_err_jv"],
|
||||||
|
)
|
||||||
|
create_err_and_its_journals(companies)
|
||||||
|
|
||||||
|
|
||||||
|
def auto_create_exchange_rate_revaluation_weekly() -> None:
|
||||||
|
"""
|
||||||
|
Executed by background job
|
||||||
|
"""
|
||||||
|
companies = frappe.db.get_all(
|
||||||
|
"Company",
|
||||||
|
filters={"auto_exchange_rate_revaluation": 1, "auto_err_frequency": "Weekly"},
|
||||||
|
fields=["name", "submit_err_jv"],
|
||||||
|
)
|
||||||
|
create_err_and_its_journals(companies)
|
||||||
|
|
||||||
|
|
||||||
def get_payment_ledger_entries(gl_entries, cancel=0):
|
def get_payment_ledger_entries(gl_entries, cancel=0):
|
||||||
ple_map = []
|
ple_map = []
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ def post_depreciation_entries(date=None):
|
|||||||
date = today()
|
date = today()
|
||||||
|
|
||||||
failed_asset_names = []
|
failed_asset_names = []
|
||||||
|
error_log_names = []
|
||||||
|
|
||||||
for asset_name in get_depreciable_assets(date):
|
for asset_name in get_depreciable_assets(date):
|
||||||
asset_doc = frappe.get_doc("Asset", asset_name)
|
asset_doc = frappe.get_doc("Asset", asset_name)
|
||||||
@@ -50,10 +51,12 @@ def post_depreciation_entries(date=None):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
failed_asset_names.append(asset_name)
|
failed_asset_names.append(asset_name)
|
||||||
|
error_log = frappe.log_error(e)
|
||||||
|
error_log_names.append(error_log.name)
|
||||||
|
|
||||||
if failed_asset_names:
|
if failed_asset_names:
|
||||||
set_depr_entry_posting_status_for_failed_assets(failed_asset_names)
|
set_depr_entry_posting_status_for_failed_assets(failed_asset_names)
|
||||||
notify_depr_entry_posting_error(failed_asset_names)
|
notify_depr_entry_posting_error(failed_asset_names, error_log_names)
|
||||||
|
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
|
|
||||||
@@ -239,7 +242,7 @@ def set_depr_entry_posting_status_for_failed_assets(failed_asset_names):
|
|||||||
frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Failed")
|
frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Failed")
|
||||||
|
|
||||||
|
|
||||||
def notify_depr_entry_posting_error(failed_asset_names):
|
def notify_depr_entry_posting_error(failed_asset_names, error_log_names):
|
||||||
recipients = get_users_with_role("Accounts Manager")
|
recipients = get_users_with_role("Accounts Manager")
|
||||||
|
|
||||||
if not recipients:
|
if not recipients:
|
||||||
@@ -247,7 +250,8 @@ def notify_depr_entry_posting_error(failed_asset_names):
|
|||||||
|
|
||||||
subject = _("Error while posting depreciation entries")
|
subject = _("Error while posting depreciation entries")
|
||||||
|
|
||||||
asset_links = get_comma_separated_asset_links(failed_asset_names)
|
asset_links = get_comma_separated_links(failed_asset_names, "Asset")
|
||||||
|
error_log_links = get_comma_separated_links(error_log_names, "Error Log")
|
||||||
|
|
||||||
message = (
|
message = (
|
||||||
_("Hello,")
|
_("Hello,")
|
||||||
@@ -257,23 +261,26 @@ def notify_depr_entry_posting_error(failed_asset_names):
|
|||||||
)
|
)
|
||||||
+ "."
|
+ "."
|
||||||
+ "<br><br>"
|
+ "<br><br>"
|
||||||
+ _(
|
+ _("Here are the error logs for the aforementioned failed depreciation entries: {0}").format(
|
||||||
"Please raise a support ticket and share this email, or forward this email to your development team so that they can find the issue in the developer console by manually creating the depreciation entry via the asset's depreciation schedule table."
|
error_log_links
|
||||||
)
|
)
|
||||||
|
+ "."
|
||||||
|
+ "<br><br>"
|
||||||
|
+ _("Please share this email with your support team so that they can find and fix the issue.")
|
||||||
)
|
)
|
||||||
|
|
||||||
frappe.sendmail(recipients=recipients, subject=subject, message=message)
|
frappe.sendmail(recipients=recipients, subject=subject, message=message)
|
||||||
|
|
||||||
|
|
||||||
def get_comma_separated_asset_links(asset_names):
|
def get_comma_separated_links(names, doctype):
|
||||||
asset_links = []
|
links = []
|
||||||
|
|
||||||
for asset_name in asset_names:
|
for name in names:
|
||||||
asset_links.append(get_link_to_form("Asset", asset_name))
|
links.append(get_link_to_form(doctype, name))
|
||||||
|
|
||||||
asset_links = ", ".join(asset_links)
|
links = ", ".join(links)
|
||||||
|
|
||||||
return asset_links
|
return links
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -66,8 +66,6 @@ frappe.ui.form.on("Supplier", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Supplier' }
|
|
||||||
|
|
||||||
if (frappe.defaults.get_default("supp_master_name") != "Naming Series") {
|
if (frappe.defaults.get_default("supp_master_name") != "Naming Series") {
|
||||||
frm.toggle_display("naming_series", false);
|
frm.toggle_display("naming_series", false);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -437,18 +437,23 @@ class BuyingController(SubcontractingController):
|
|||||||
# validate rate with ref PR
|
# validate rate with ref PR
|
||||||
|
|
||||||
def validate_rejected_warehouse(self):
|
def validate_rejected_warehouse(self):
|
||||||
for d in self.get("items"):
|
for item in self.get("items"):
|
||||||
if flt(d.rejected_qty) and not d.rejected_warehouse:
|
if flt(item.rejected_qty) and not item.rejected_warehouse:
|
||||||
if self.rejected_warehouse:
|
if self.rejected_warehouse:
|
||||||
d.rejected_warehouse = self.rejected_warehouse
|
item.rejected_warehouse = self.rejected_warehouse
|
||||||
|
|
||||||
if not d.rejected_warehouse:
|
if not item.rejected_warehouse:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Row #{0}: Rejected Warehouse is mandatory against rejected Item {1}").format(
|
_("Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1}").format(
|
||||||
d.idx, d.item_code
|
item.idx, item.item_code
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if item.get("rejected_warehouse") and (item.get("rejected_warehouse") == item.get("warehouse")):
|
||||||
|
frappe.throw(
|
||||||
|
_("Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same").format(item.idx)
|
||||||
|
)
|
||||||
|
|
||||||
# validate accepted and rejected qty
|
# validate accepted and rejected qty
|
||||||
def validate_accepted_rejected_qty(self):
|
def validate_accepted_rejected_qty(self):
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
|
|||||||
@@ -669,7 +669,11 @@ def get_filters(
|
|||||||
if reference_voucher_detail_no:
|
if reference_voucher_detail_no:
|
||||||
filters["voucher_detail_no"] = reference_voucher_detail_no
|
filters["voucher_detail_no"] = reference_voucher_detail_no
|
||||||
|
|
||||||
if item_row and item_row.get("warehouse"):
|
if (
|
||||||
|
voucher_type in ["Purchase Receipt", "Purchase Invoice"]
|
||||||
|
and item_row
|
||||||
|
and item_row.get("warehouse")
|
||||||
|
):
|
||||||
filters["warehouse"] = item_row.get("warehouse")
|
filters["warehouse"] = item_row.get("warehouse")
|
||||||
|
|
||||||
return filters
|
return filters
|
||||||
|
|||||||
@@ -201,6 +201,12 @@ class StockController(AccountsController):
|
|||||||
warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"]
|
warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"]
|
||||||
|
|
||||||
expense_account = frappe.get_cached_value("Company", self.company, "default_expense_account")
|
expense_account = frappe.get_cached_value("Company", self.company, "default_expense_account")
|
||||||
|
if not expense_account:
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"Please set default cost of goods sold account in company {0} for booking rounding gain and loss during stock transfer"
|
||||||
|
).format(frappe.bold(self.company))
|
||||||
|
)
|
||||||
|
|
||||||
gl_list.append(
|
gl_list.append(
|
||||||
self.get_gl_dict(
|
self.get_gl_dict(
|
||||||
|
|||||||
@@ -30,11 +30,6 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller
|
|||||||
var me = this;
|
var me = this;
|
||||||
let doc = this.frm.doc;
|
let doc = this.frm.doc;
|
||||||
erpnext.toggle_naming_series();
|
erpnext.toggle_naming_series();
|
||||||
frappe.dynamic_link = {
|
|
||||||
doc: doc,
|
|
||||||
fieldname: 'name',
|
|
||||||
doctype: 'Lead'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) {
|
if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) {
|
||||||
this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create"));
|
this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create"));
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Prospect', {
|
frappe.ui.form.on('Prospect', {
|
||||||
refresh (frm) {
|
refresh (frm) {
|
||||||
frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: frm.doctype };
|
|
||||||
|
|
||||||
if (!frm.is_new() && frappe.boot.user.can_create.includes("Customer")) {
|
if (!frm.is_new() && frappe.boot.user.can_create.includes("Customer")) {
|
||||||
frm.add_custom_button(__("Customer"), function() {
|
frm.add_custom_button(__("Customer"), function() {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
|
|||||||
@@ -415,6 +415,10 @@ scheduler_events = {
|
|||||||
"erpnext.selling.doctype.quotation.quotation.set_expired_status",
|
"erpnext.selling.doctype.quotation.quotation.set_expired_status",
|
||||||
"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status",
|
"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status",
|
||||||
"erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email",
|
"erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email",
|
||||||
|
"erpnext.accounts.utils.auto_create_exchange_rate_revaluation_daily",
|
||||||
|
],
|
||||||
|
"weekly": [
|
||||||
|
"erpnext.accounts.utils.auto_create_exchange_rate_revaluation_weekly",
|
||||||
],
|
],
|
||||||
"daily_long": [
|
"daily_long": [
|
||||||
"erpnext.setup.doctype.email_digest.email_digest.send",
|
"erpnext.setup.doctype.email_digest.email_digest.send",
|
||||||
|
|||||||
@@ -131,8 +131,6 @@ frappe.ui.form.on("Customer", {
|
|||||||
erpnext.toggle_naming_series();
|
erpnext.toggle_naming_series();
|
||||||
}
|
}
|
||||||
|
|
||||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Customer'}
|
|
||||||
|
|
||||||
if(!frm.doc.__islocal) {
|
if(!frm.doc.__islocal) {
|
||||||
frappe.contacts.render_address_and_contact(frm);
|
frappe.contacts.render_address_and_contact(frm);
|
||||||
|
|
||||||
|
|||||||
@@ -81,8 +81,6 @@ frappe.ui.form.on("Company", {
|
|||||||
disbale_coa_fields(frm);
|
disbale_coa_fields(frm);
|
||||||
frappe.contacts.render_address_and_contact(frm);
|
frappe.contacts.render_address_and_contact(frm);
|
||||||
|
|
||||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Company'}
|
|
||||||
|
|
||||||
if (frappe.perm.has_perm("Cost Center", 0, 'read')) {
|
if (frappe.perm.has_perm("Cost Center", 0, 'read')) {
|
||||||
frm.add_custom_button(__('Cost Centers'), function() {
|
frm.add_custom_button(__('Cost Centers'), function() {
|
||||||
frappe.set_route('Tree', 'Cost Center', {'company': frm.doc.name});
|
frappe.set_route('Tree', 'Cost Center', {'company': frm.doc.name});
|
||||||
|
|||||||
@@ -95,6 +95,10 @@
|
|||||||
"depreciation_cost_center",
|
"depreciation_cost_center",
|
||||||
"capital_work_in_progress_account",
|
"capital_work_in_progress_account",
|
||||||
"asset_received_but_not_billed",
|
"asset_received_but_not_billed",
|
||||||
|
"exchange_rate_revaluation_settings_section",
|
||||||
|
"auto_exchange_rate_revaluation",
|
||||||
|
"auto_err_frequency",
|
||||||
|
"submit_err_jv",
|
||||||
"budget_detail",
|
"budget_detail",
|
||||||
"exception_budget_approver_role",
|
"exception_budget_approver_role",
|
||||||
"registration_info",
|
"registration_info",
|
||||||
@@ -731,6 +735,29 @@
|
|||||||
"fieldname": "book_advance_payments_in_separate_party_account",
|
"fieldname": "book_advance_payments_in_separate_party_account",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Book Advance Payments in Separate Party Account"
|
"label": "Book Advance Payments in Separate Party Account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "exchange_rate_revaluation_settings_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Exchange Rate Revaluation Settings"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "auto_exchange_rate_revaluation",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Auto Create Exchange Rate Revaluation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "auto_err_frequency",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Frequency",
|
||||||
|
"options": "Daily\nWeekly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "submit_err_jv",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Submit ERR Journals?"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-building",
|
"icon": "fa fa-building",
|
||||||
@@ -738,7 +765,7 @@
|
|||||||
"image_field": "company_logo",
|
"image_field": "company_logo",
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-06-23 18:22:27.219706",
|
"modified": "2023-07-07 05:41:41.537256",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Company",
|
"name": "Company",
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Sales Partner', {
|
frappe.ui.form.on('Sales Partner', {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Sales Partner'}
|
|
||||||
|
|
||||||
if(frm.doc.__islocal){
|
if(frm.doc.__islocal){
|
||||||
hide_field(['address_html', 'contact_html', 'address_contacts']);
|
hide_field(['address_html', 'contact_html', 'address_contacts']);
|
||||||
frappe.contacts.clear_address_and_contact(frm);
|
frappe.contacts.clear_address_and_contact(frm);
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ frappe.ui.form.on('Batch', {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
debugger
|
|
||||||
|
|
||||||
const section = frm.dashboard.add_section('', __("Stock Levels"));
|
const section = frm.dashboard.add_section('', __("Stock Levels"));
|
||||||
|
|
||||||
// sort by qty
|
// sort by qty
|
||||||
|
|||||||
@@ -59,6 +59,73 @@ class TestBatch(FrappeTestCase):
|
|||||||
|
|
||||||
return receipt
|
return receipt
|
||||||
|
|
||||||
|
def test_batch_stock_levels(self, batch_qty=100):
|
||||||
|
"""Test automated batch creation from Purchase Receipt"""
|
||||||
|
self.make_batch_item("ITEM-BATCH-1")
|
||||||
|
|
||||||
|
receipt = frappe.get_doc(
|
||||||
|
dict(
|
||||||
|
doctype="Purchase Receipt",
|
||||||
|
supplier="_Test Supplier",
|
||||||
|
company="_Test Company",
|
||||||
|
items=[dict(item_code="ITEM-BATCH-1", qty=10, rate=10, warehouse="Stores - _TC")],
|
||||||
|
)
|
||||||
|
).insert()
|
||||||
|
receipt.submit()
|
||||||
|
|
||||||
|
receipt.load_from_db()
|
||||||
|
batch_no = get_batch_from_bundle(receipt.items[0].serial_and_batch_bundle)
|
||||||
|
|
||||||
|
bundle_id = (
|
||||||
|
SerialBatchCreation(
|
||||||
|
{
|
||||||
|
"item_code": "ITEM-BATCH-1",
|
||||||
|
"warehouse": "_Test Warehouse - _TC",
|
||||||
|
"actual_qty": 20,
|
||||||
|
"voucher_type": "Purchase Receipt",
|
||||||
|
"batches": frappe._dict({batch_no: 20}),
|
||||||
|
"type_of_transaction": "Inward",
|
||||||
|
"company": receipt.company,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.make_serial_and_batch_bundle()
|
||||||
|
.name
|
||||||
|
)
|
||||||
|
|
||||||
|
receipt2 = frappe.get_doc(
|
||||||
|
dict(
|
||||||
|
doctype="Purchase Receipt",
|
||||||
|
supplier="_Test Supplier",
|
||||||
|
company="_Test Company",
|
||||||
|
items=[
|
||||||
|
dict(
|
||||||
|
item_code="ITEM-BATCH-1",
|
||||||
|
qty=20,
|
||||||
|
rate=10,
|
||||||
|
warehouse="_Test Warehouse - _TC",
|
||||||
|
serial_and_batch_bundle=bundle_id,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
).insert()
|
||||||
|
receipt2.submit()
|
||||||
|
|
||||||
|
receipt.load_from_db()
|
||||||
|
receipt2.load_from_db()
|
||||||
|
|
||||||
|
self.assertTrue(receipt.items[0].serial_and_batch_bundle)
|
||||||
|
self.assertTrue(receipt2.items[0].serial_and_batch_bundle)
|
||||||
|
|
||||||
|
batchwise_qty = frappe._dict({})
|
||||||
|
for receipt in [receipt, receipt2]:
|
||||||
|
batch_no = get_batch_from_bundle(receipt.items[0].serial_and_batch_bundle)
|
||||||
|
key = (batch_no, receipt.items[0].warehouse)
|
||||||
|
batchwise_qty[key] = receipt.items[0].qty
|
||||||
|
|
||||||
|
batches = get_batch_qty(batch_no)
|
||||||
|
for d in batches:
|
||||||
|
self.assertEqual(d.qty, batchwise_qty[(d.batch_no, d.warehouse)])
|
||||||
|
|
||||||
def test_stock_entry_incoming(self):
|
def test_stock_entry_incoming(self):
|
||||||
"""Test batch creation via Stock Entry (Work Order)"""
|
"""Test batch creation via Stock Entry (Work Order)"""
|
||||||
|
|
||||||
|
|||||||
@@ -318,6 +318,37 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
self.assertEqual(dn.per_returned, 100)
|
self.assertEqual(dn.per_returned, 100)
|
||||||
self.assertEqual(dn.status, "Return Issued")
|
self.assertEqual(dn.status, "Return Issued")
|
||||||
|
|
||||||
|
def test_delivery_note_return_valuation_on_different_warehuose(self):
|
||||||
|
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||||
|
|
||||||
|
company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
|
||||||
|
item_code = "Test Return Valuation For DN"
|
||||||
|
make_item("Test Return Valuation For DN", {"is_stock_item": 1})
|
||||||
|
return_warehouse = create_warehouse("Returned Test Warehouse", company=company)
|
||||||
|
|
||||||
|
make_stock_entry(item_code=item_code, target="Stores - TCP1", qty=5, basic_rate=150)
|
||||||
|
|
||||||
|
dn = create_delivery_note(
|
||||||
|
item_code=item_code,
|
||||||
|
qty=5,
|
||||||
|
rate=500,
|
||||||
|
warehouse="Stores - TCP1",
|
||||||
|
company=company,
|
||||||
|
expense_account="Cost of Goods Sold - TCP1",
|
||||||
|
cost_center="Main - TCP1",
|
||||||
|
)
|
||||||
|
|
||||||
|
dn.submit()
|
||||||
|
self.assertEqual(dn.items[0].incoming_rate, 150)
|
||||||
|
|
||||||
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
|
|
||||||
|
return_dn = make_return_doc(dn.doctype, dn.name)
|
||||||
|
return_dn.items[0].warehouse = return_warehouse
|
||||||
|
return_dn.save().submit()
|
||||||
|
|
||||||
|
self.assertEqual(return_dn.items[0].incoming_rate, 150)
|
||||||
|
|
||||||
def test_return_single_item_from_bundled_items(self):
|
def test_return_single_item_from_bundled_items(self):
|
||||||
company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
|
company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Manufacturer', {
|
frappe.ui.form.on('Manufacturer', {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' };
|
|
||||||
if (frm.doc.__islocal) {
|
if (frm.doc.__islocal) {
|
||||||
hide_field(['address_html','contact_html']);
|
hide_field(['address_html','contact_html']);
|
||||||
frappe.contacts.clear_address_and_contact(frm);
|
frappe.contacts.clear_address_and_contact(frm);
|
||||||
|
|||||||
@@ -438,6 +438,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "rejected_warehouse",
|
"fieldname": "rejected_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
"label": "Rejected Warehouse",
|
"label": "Rejected Warehouse",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "rejected_warehouse",
|
"oldfieldname": "rejected_warehouse",
|
||||||
@@ -1240,7 +1241,7 @@
|
|||||||
"idx": 261,
|
"idx": 261,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-06-03 16:23:20.781368",
|
"modified": "2023-07-04 17:23:17.025390",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Purchase Receipt",
|
"name": "Purchase Receipt",
|
||||||
|
|||||||
@@ -350,6 +350,15 @@ class TestPurchaseReceipt(FrappeTestCase):
|
|||||||
pr.cancel()
|
pr.cancel()
|
||||||
self.assertFalse(frappe.db.get_value("Serial No", pr_row_1_serial_no, "warehouse"))
|
self.assertFalse(frappe.db.get_value("Serial No", pr_row_1_serial_no, "warehouse"))
|
||||||
|
|
||||||
|
def test_rejected_warehouse_filter(self):
|
||||||
|
pr = frappe.copy_doc(test_records[0])
|
||||||
|
pr.get("items")[0].item_code = "_Test Serialized Item With Series"
|
||||||
|
pr.get("items")[0].qty = 3
|
||||||
|
pr.get("items")[0].rejected_qty = 2
|
||||||
|
pr.get("items")[0].received_qty = 5
|
||||||
|
pr.get("items")[0].rejected_warehouse = pr.get("items")[0].warehouse
|
||||||
|
self.assertRaises(frappe.ValidationError, pr.save)
|
||||||
|
|
||||||
def test_rejected_serial_no(self):
|
def test_rejected_serial_no(self):
|
||||||
pr = frappe.copy_doc(test_records[0])
|
pr = frappe.copy_doc(test_records[0])
|
||||||
pr.get("items")[0].item_code = "_Test Serialized Item With Series"
|
pr.get("items")[0].item_code = "_Test Serialized Item With Series"
|
||||||
|
|||||||
@@ -502,6 +502,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "rejected_warehouse",
|
"fieldname": "rejected_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
"label": "Rejected Warehouse",
|
"label": "Rejected Warehouse",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "rejected_warehouse",
|
"oldfieldname": "rejected_warehouse",
|
||||||
@@ -1058,7 +1059,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-07-02 18:40:48.152637",
|
"modified": "2023-07-04 17:22:02.830029",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Purchase Receipt Item",
|
"name": "Purchase Receipt Item",
|
||||||
|
|||||||
@@ -1272,24 +1272,29 @@ def get_reserved_batches_for_pos(kwargs):
|
|||||||
|
|
||||||
if ids:
|
if ids:
|
||||||
for d in get_serial_batch_ledgers(kwargs.item_code, docstatus=1, name=ids):
|
for d in get_serial_batch_ledgers(kwargs.item_code, docstatus=1, name=ids):
|
||||||
if d.batch_no not in pos_batches:
|
key = (d.batch_no, d.warehouse)
|
||||||
pos_batches[d.batch_no] = frappe._dict(
|
if key not in pos_batches:
|
||||||
|
pos_batches[key] = frappe._dict(
|
||||||
{
|
{
|
||||||
"qty": d.qty,
|
"qty": d.qty,
|
||||||
"warehouse": d.warehouse,
|
"warehouse": d.warehouse,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
pos_batches[d.batch_no].qty += d.qty
|
pos_batches[key].qty += d.qty
|
||||||
|
|
||||||
for row in pos_invoices:
|
for row in pos_invoices:
|
||||||
if not row.batch_no:
|
if not row.batch_no:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if row.batch_no in pos_batches:
|
if kwargs.get("batch_no") and row.batch_no != kwargs.get("batch_no"):
|
||||||
pos_batches[row.batch_no] -= row.qty * -1 if row.is_return else row.qty
|
continue
|
||||||
|
|
||||||
|
key = (row.batch_no, row.warehouse)
|
||||||
|
if key in pos_batches:
|
||||||
|
pos_batches[key] -= row.qty * -1 if row.is_return else row.qty
|
||||||
else:
|
else:
|
||||||
pos_batches[row.batch_no] = frappe._dict(
|
pos_batches[key] = frappe._dict(
|
||||||
{
|
{
|
||||||
"qty": (row.qty * -1 if row.is_return else row.qty),
|
"qty": (row.qty * -1 if row.is_return else row.qty),
|
||||||
"warehouse": row.warehouse,
|
"warehouse": row.warehouse,
|
||||||
@@ -1309,6 +1314,7 @@ def get_auto_batch_nos(kwargs):
|
|||||||
update_available_batches(available_batches, stock_ledgers_batches, pos_invoice_batches)
|
update_available_batches(available_batches, stock_ledgers_batches, pos_invoice_batches)
|
||||||
|
|
||||||
available_batches = list(filter(lambda x: x.qty > 0, available_batches))
|
available_batches = list(filter(lambda x: x.qty > 0, available_batches))
|
||||||
|
|
||||||
if not qty:
|
if not qty:
|
||||||
return available_batches
|
return available_batches
|
||||||
|
|
||||||
@@ -1351,10 +1357,11 @@ def get_qty_based_available_batches(available_batches, qty):
|
|||||||
def update_available_batches(available_batches, reserved_batches=None, pos_invoice_batches=None):
|
def update_available_batches(available_batches, reserved_batches=None, pos_invoice_batches=None):
|
||||||
for batches in [reserved_batches, pos_invoice_batches]:
|
for batches in [reserved_batches, pos_invoice_batches]:
|
||||||
if batches:
|
if batches:
|
||||||
for batch_no, data in batches.items():
|
for key, data in batches.items():
|
||||||
|
batch_no, warehouse = key
|
||||||
batch_not_exists = True
|
batch_not_exists = True
|
||||||
for batch in available_batches:
|
for batch in available_batches:
|
||||||
if batch.batch_no == batch_no and batch.warehouse == data.warehouse:
|
if batch.batch_no == batch_no and batch.warehouse == warehouse:
|
||||||
batch.qty += data.qty
|
batch.qty += data.qty
|
||||||
batch_not_exists = False
|
batch_not_exists = False
|
||||||
|
|
||||||
@@ -1563,7 +1570,7 @@ def get_stock_ledgers_batches(kwargs):
|
|||||||
.groupby(stock_ledger_entry.batch_no, stock_ledger_entry.warehouse)
|
.groupby(stock_ledger_entry.batch_no, stock_ledger_entry.warehouse)
|
||||||
)
|
)
|
||||||
|
|
||||||
for field in ["warehouse", "item_code"]:
|
for field in ["warehouse", "item_code", "batch_no"]:
|
||||||
if not kwargs.get(field):
|
if not kwargs.get(field):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -1582,6 +1589,10 @@ def get_stock_ledgers_batches(kwargs):
|
|||||||
data = query.run(as_dict=True)
|
data = query.run(as_dict=True)
|
||||||
batches = {}
|
batches = {}
|
||||||
for d in data:
|
for d in data:
|
||||||
batches[d.batch_no] = d
|
key = (d.batch_no, d.warehouse)
|
||||||
|
if key not in batches:
|
||||||
|
batches[key] = d
|
||||||
|
else:
|
||||||
|
batches[key].qty += d.qty
|
||||||
|
|
||||||
return batches
|
return batches
|
||||||
|
|||||||
@@ -83,12 +83,6 @@ frappe.ui.form.on("Warehouse", {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frm.toggle_enable(["is_group", "company"], false);
|
frm.toggle_enable(["is_group", "company"], false);
|
||||||
|
|
||||||
frappe.dynamic_link = {
|
|
||||||
doc: frm.doc,
|
|
||||||
fieldname: "name",
|
|
||||||
doctype: "Warehouse",
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -251,6 +251,7 @@
|
|||||||
"description": "Sets 'Rejected Warehouse' in each row of the Items table.",
|
"description": "Sets 'Rejected Warehouse' in each row of the Items table.",
|
||||||
"fieldname": "rejected_warehouse",
|
"fieldname": "rejected_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
"label": "Rejected Warehouse",
|
"label": "Rejected Warehouse",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Warehouse",
|
"options": "Warehouse",
|
||||||
@@ -630,7 +631,7 @@
|
|||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-06-03 16:18:39.088518",
|
"modified": "2023-07-06 18:43:16.171842",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Subcontracting",
|
"module": "Subcontracting",
|
||||||
"name": "Subcontracting Receipt",
|
"name": "Subcontracting Receipt",
|
||||||
|
|||||||
@@ -192,13 +192,23 @@ class SubcontractingReceipt(SubcontractingController):
|
|||||||
self.total = total_amount
|
self.total = total_amount
|
||||||
|
|
||||||
def validate_rejected_warehouse(self):
|
def validate_rejected_warehouse(self):
|
||||||
if not self.rejected_warehouse:
|
for item in self.items:
|
||||||
for item in self.items:
|
if flt(item.rejected_qty) and not item.rejected_warehouse:
|
||||||
if item.rejected_qty:
|
if self.rejected_warehouse:
|
||||||
|
item.rejected_warehouse = self.rejected_warehouse
|
||||||
|
|
||||||
|
if not item.rejected_warehouse:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Rejected Warehouse is mandatory against rejected Item {0}").format(item.item_code)
|
_("Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1}").format(
|
||||||
|
item.idx, item.item_code
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if item.get("rejected_warehouse") and (item.get("rejected_warehouse") == item.get("warehouse")):
|
||||||
|
frappe.throw(
|
||||||
|
_("Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same").format(item.idx)
|
||||||
|
)
|
||||||
|
|
||||||
def validate_available_qty_for_consumption(self):
|
def validate_available_qty_for_consumption(self):
|
||||||
for item in self.get("supplied_items"):
|
for item in self.get("supplied_items"):
|
||||||
precision = item.precision("consumed_qty")
|
precision = item.precision("consumed_qty")
|
||||||
|
|||||||
@@ -254,6 +254,7 @@
|
|||||||
"depends_on": "eval: !parent.is_return",
|
"depends_on": "eval: !parent.is_return",
|
||||||
"fieldname": "rejected_warehouse",
|
"fieldname": "rejected_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
"label": "Rejected Warehouse",
|
"label": "Rejected Warehouse",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Warehouse",
|
"options": "Warehouse",
|
||||||
@@ -494,7 +495,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-03-12 14:00:41.418681",
|
"modified": "2023-07-06 18:43:45.599761",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Subcontracting",
|
"module": "Subcontracting",
|
||||||
"name": "Subcontracting Receipt Item",
|
"name": "Subcontracting Receipt Item",
|
||||||
|
|||||||
Reference in New Issue
Block a user