mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-15 11:09:17 +00:00
Merge branch 'v12-pre-release' into version-12
This commit is contained in:
@@ -5,7 +5,7 @@ import frappe
|
|||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
__version__ = '12.26.0'
|
__version__ = '12.27.0'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
|||||||
@@ -271,6 +271,7 @@
|
|||||||
"label": "Debit",
|
"label": "Debit",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
|
"options": "currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
@@ -304,6 +305,7 @@
|
|||||||
"label": "Credit",
|
"label": "Credit",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
|
"options": "currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
@@ -632,6 +634,7 @@
|
|||||||
"label": "Allocated Amount",
|
"label": "Allocated Amount",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
|
"options": "currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
@@ -731,6 +734,7 @@
|
|||||||
"label": "Unallocated Amount",
|
"label": "Unallocated Amount",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
|
"options": "currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
@@ -755,7 +759,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2019-05-11 05:27:55.244721",
|
"modified": "2021-11-26 12:44:55.244721",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Bank Transaction",
|
"name": "Bank Transaction",
|
||||||
|
|||||||
@@ -59,6 +59,12 @@
|
|||||||
"year_end_date": "2021-12-31",
|
"year_end_date": "2021-12-31",
|
||||||
"year_start_date": "2021-01-01"
|
"year_start_date": "2021-01-01"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Fiscal Year",
|
||||||
|
"year": "_Test Fiscal Year 2022",
|
||||||
|
"year_end_date": "2022-12-31",
|
||||||
|
"year_start_date": "2022-01-01"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"doctype": "Fiscal Year",
|
"doctype": "Fiscal Year",
|
||||||
"year": "_Test Short Fiscal Year 2021",
|
"year": "_Test Short Fiscal Year 2021",
|
||||||
|
|||||||
9
erpnext/change_log/v12/v12_27_0.md
Normal file
9
erpnext/change_log/v12/v12_27_0.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
## ERPNext Version 12.27.0 Release Notes
|
||||||
|
|
||||||
|
### Fixes & Enhancements
|
||||||
|
- Always expect signature in webhook requests for WooCommerce ([#28367](https://github.com/frappe/erpnext/pull/28367))
|
||||||
|
- Debit & Credit currency in bank transaction ([#28574](https://github.com/frappe/erpnext/pull/28574))
|
||||||
|
- Incorrect balance in "Warehouse Wise Item Balance and Age" report ([#28583](https://github.com/frappe/erpnext/pull/28583))
|
||||||
|
- Check if gst_category exists while validating GSTIN ([#28065](https://github.com/frappe/erpnext/pull/28065))
|
||||||
|
- Skip empty rows while updating unsaved BOM cost ([#28136](https://github.com/frappe/erpnext/pull/28136))
|
||||||
|
- Remove warehouse filter on Batch field for Material Receipt ([#28195](https://github.com/frappe/erpnext/pull/28195))
|
||||||
@@ -14,8 +14,7 @@ def verify_request():
|
|||||||
)
|
)
|
||||||
|
|
||||||
if frappe.request.data and \
|
if frappe.request.data and \
|
||||||
frappe.get_request_header("X-Wc-Webhook-Signature") and \
|
not sig == frappe.get_request_header("X-Wc-Webhook-Signature", "").encode():
|
||||||
not sig == bytes(frappe.get_request_header("X-Wc-Webhook-Signature").encode()):
|
|
||||||
frappe.throw(_("Unverified Webhook Data"))
|
frappe.throw(_("Unverified Webhook Data"))
|
||||||
frappe.set_user(woocommerce_settings.creation_user)
|
frappe.set_user(woocommerce_settings.creation_user)
|
||||||
|
|
||||||
|
|||||||
@@ -64,5 +64,8 @@ def dump_request_data(data, event="create/order"):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def resync(method, name, request_data):
|
def resync(method, name, request_data):
|
||||||
frappe.db.set_value("Shopify Log", name, "status", "Queued", update_modified=False)
|
frappe.db.set_value("Shopify Log", name, "status", "Queued", update_modified=False)
|
||||||
|
if not method.startswith("erpnext.erpnext_integrations.connectors.shopify_connection"):
|
||||||
|
return
|
||||||
|
|
||||||
frappe.enqueue(method=method, queue='short', timeout=300, is_async=True,
|
frappe.enqueue(method=method, queue='short', timeout=300, is_async=True,
|
||||||
**{"order": json.loads(request_data), "request_id": name})
|
**{"order": json.loads(request_data), "request_id": name})
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ def validate_webhooks_request(doctype, hmac_key, secret_key='secret'):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if frappe.request.data and \
|
if frappe.request.data and \
|
||||||
frappe.get_request_header(hmac_key) and \
|
|
||||||
not sig == bytes(frappe.get_request_header(hmac_key).encode()):
|
not sig == bytes(frappe.get_request_header(hmac_key).encode()):
|
||||||
frappe.throw(_("Unverified Webhook Data"))
|
frappe.throw(_("Unverified Webhook Data"))
|
||||||
frappe.set_user(settings.modified_by)
|
frappe.set_user(settings.modified_by)
|
||||||
|
|||||||
@@ -240,6 +240,9 @@ class BOM(WebsiteGenerator):
|
|||||||
existing_bom_cost = self.total_cost
|
existing_bom_cost = self.total_cost
|
||||||
|
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
|
if not d.item_code:
|
||||||
|
continue
|
||||||
|
|
||||||
rate = self.get_rm_rate({
|
rate = self.get_rm_rate({
|
||||||
"company": self.company,
|
"company": self.company,
|
||||||
"item_code": d.item_code,
|
"item_code": d.item_code,
|
||||||
@@ -549,7 +552,7 @@ class BOM(WebsiteGenerator):
|
|||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if d.bom_no:
|
if d.bom_no:
|
||||||
self.get_child_exploded_items(d.bom_no, d.stock_qty)
|
self.get_child_exploded_items(d.bom_no, d.stock_qty)
|
||||||
else:
|
elif d.item_code:
|
||||||
self.add_to_cur_exploded_items(frappe._dict({
|
self.add_to_cur_exploded_items(frappe._dict({
|
||||||
'item_code' : d.item_code,
|
'item_code' : d.item_code,
|
||||||
'item_name' : d.item_name,
|
'item_name' : d.item_name,
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ def get_bom_stock(filters):
|
|||||||
GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
|
GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
|
||||||
|
|
||||||
def get_manufacturer_records():
|
def get_manufacturer_records():
|
||||||
details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "parent"])
|
details = frappe.get_all('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "parent"])
|
||||||
manufacture_details = frappe._dict()
|
manufacture_details = frappe._dict()
|
||||||
for detail in details:
|
for detail in details:
|
||||||
dic = manufacture_details.setdefault(detail.get('parent'), {})
|
dic = manufacture_details.setdefault(detail.get('parent'), {})
|
||||||
|
|||||||
@@ -30,12 +30,13 @@ def validate_gstin_for_india(doc, method):
|
|||||||
|
|
||||||
gst_category = []
|
gst_category = []
|
||||||
|
|
||||||
if len(doc.links):
|
if hasattr(doc, 'gst_category'):
|
||||||
link_doctype = doc.links[0].get("link_doctype")
|
if len(doc.links):
|
||||||
link_name = doc.links[0].get("link_name")
|
link_doctype = doc.links[0].get("link_doctype")
|
||||||
|
link_name = doc.links[0].get("link_name")
|
||||||
|
|
||||||
if link_doctype in ["Customer", "Supplier"]:
|
if link_doctype in ["Customer", "Supplier"]:
|
||||||
gst_category = frappe.db.get_value(link_doctype, {'name': link_name}, ['gst_category'])
|
gst_category = frappe.db.get_value(link_doctype, {'name': link_name}, ['gst_category'])
|
||||||
|
|
||||||
doc.gstin = doc.gstin.upper().strip()
|
doc.gstin = doc.gstin.upper().strip()
|
||||||
if not doc.gstin or doc.gstin == 'NA':
|
if not doc.gstin or doc.gstin == 'NA':
|
||||||
@@ -66,12 +67,11 @@ def validate_tax_category(doc, method):
|
|||||||
frappe.throw(_("Intra State tax category for GST State {0} already exists").format(doc.gst_state))
|
frappe.throw(_("Intra State tax category for GST State {0} already exists").format(doc.gst_state))
|
||||||
|
|
||||||
def update_gst_category(doc, method):
|
def update_gst_category(doc, method):
|
||||||
for link in doc.links:
|
if hasattr(doc, 'gst_category'):
|
||||||
if link.link_doctype in ['Customer', 'Supplier']:
|
for link in doc.links:
|
||||||
if doc.get('gstin'):
|
if link.link_doctype in ['Customer', 'Supplier']:
|
||||||
frappe.db.sql("""
|
if doc.get('gstin'):
|
||||||
UPDATE `tab{0}` SET gst_category = %s WHERE name = %s AND gst_category = 'Unregistered'
|
frappe.db.set_value(link.link_doctype, {'name': link.link_name, 'gst_category': 'Unregistered'}, 'gst_category', 'Registered Regular')
|
||||||
""".format(link.link_doctype), ("Registered Regular", link.link_name)) #nosec
|
|
||||||
|
|
||||||
def set_gst_state_and_state_number(doc):
|
def set_gst_state_and_state_number(doc):
|
||||||
if not doc.gst_state:
|
if not doc.gst_state:
|
||||||
|
|||||||
@@ -104,14 +104,14 @@ def set_address_details(row, special_characters):
|
|||||||
row.update({'ship_to_state': row.to_state})
|
row.update({'ship_to_state': row.to_state})
|
||||||
|
|
||||||
def set_taxes(row, filters):
|
def set_taxes(row, filters):
|
||||||
taxes = frappe.get_list("Sales Taxes and Charges",
|
taxes = frappe.get_all("Sales Taxes and Charges",
|
||||||
filters={
|
filters={
|
||||||
'parent': row.dn_id
|
'parent': row.dn_id
|
||||||
},
|
},
|
||||||
fields=('item_wise_tax_detail', 'account_head'))
|
fields=('item_wise_tax_detail', 'account_head'))
|
||||||
|
|
||||||
account_list = ["cgst_account", "sgst_account", "igst_account", "cess_account"]
|
account_list = ["cgst_account", "sgst_account", "igst_account", "cess_account"]
|
||||||
taxes_list = frappe.get_list("GST Account",
|
taxes_list = frappe.get_all("GST Account",
|
||||||
filters={
|
filters={
|
||||||
"parent": "GST Settings",
|
"parent": "GST Settings",
|
||||||
"company": filters.company
|
"company": filters.company
|
||||||
|
|||||||
@@ -175,7 +175,9 @@ def add_new_address(doc):
|
|||||||
def create_lead_for_item_inquiry(lead, subject, message):
|
def create_lead_for_item_inquiry(lead, subject, message):
|
||||||
lead = frappe.parse_json(lead)
|
lead = frappe.parse_json(lead)
|
||||||
lead_doc = frappe.new_doc('Lead')
|
lead_doc = frappe.new_doc('Lead')
|
||||||
lead_doc.update(lead)
|
for fieldname in ("lead_name", "company_name", "email_id", "phone"):
|
||||||
|
lead_doc.set(fieldname, lead.get(fieldname))
|
||||||
|
|
||||||
lead_doc.set('lead_owner', '')
|
lead_doc.set('lead_owner', '')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -206,10 +206,11 @@ class Item(WebsiteGenerator):
|
|||||||
'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5))
|
'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5))
|
||||||
|
|
||||||
def validate_website_image(self):
|
def validate_website_image(self):
|
||||||
|
"""Validate if the website image is a public file"""
|
||||||
|
|
||||||
if frappe.flags.in_import:
|
if frappe.flags.in_import:
|
||||||
return
|
return
|
||||||
|
|
||||||
"""Validate if the website image is a public file"""
|
|
||||||
auto_set_website_image = False
|
auto_set_website_image = False
|
||||||
if not self.website_image and self.image:
|
if not self.website_image and self.image:
|
||||||
auto_set_website_image = True
|
auto_set_website_image = True
|
||||||
@@ -239,10 +240,11 @@ class Item(WebsiteGenerator):
|
|||||||
self.website_image = None
|
self.website_image = None
|
||||||
|
|
||||||
def make_thumbnail(self):
|
def make_thumbnail(self):
|
||||||
|
"""Make a thumbnail of `website_image`"""
|
||||||
|
|
||||||
if frappe.flags.in_import:
|
if frappe.flags.in_import:
|
||||||
return
|
return
|
||||||
|
|
||||||
"""Make a thumbnail of `website_image`"""
|
|
||||||
import requests.exceptions
|
import requests.exceptions
|
||||||
|
|
||||||
if not self.is_new() and self.website_image != frappe.db.get_value(self.doctype, self.name, "website_image"):
|
if not self.is_new() and self.website_image != frappe.db.get_value(self.doctype, self.name, "website_image"):
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ class TestItem(unittest.TestCase):
|
|||||||
item_doc.save()
|
item_doc.save()
|
||||||
|
|
||||||
# Check values saved correctly
|
# Check values saved correctly
|
||||||
barcodes = frappe.get_list(
|
barcodes = frappe.get_all(
|
||||||
'Item Barcode',
|
'Item Barcode',
|
||||||
fields=['barcode', 'barcode_type'],
|
fields=['barcode', 'barcode_type'],
|
||||||
filters={'parent': item_code})
|
filters={'parent': item_code})
|
||||||
|
|||||||
@@ -78,7 +78,11 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filters["warehouse"] = item.s_warehouse || item.t_warehouse;
|
// User could want to select a manually created empty batch (no warehouse)
|
||||||
|
// or a pre-existing batch
|
||||||
|
if (frm.doc.purpose != "Material Receipt") {
|
||||||
|
filters["warehouse"] = item.s_warehouse || item.t_warehouse;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
query : "erpnext.controllers.queries.get_batch_no",
|
query : "erpnext.controllers.queries.get_batch_no",
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ def execute(filters=None):
|
|||||||
item_balance.setdefault((item, item_map[item]["item_group"]), [])
|
item_balance.setdefault((item, item_map[item]["item_group"]), [])
|
||||||
total_stock_value = 0.00
|
total_stock_value = 0.00
|
||||||
for wh in warehouse_list:
|
for wh in warehouse_list:
|
||||||
row += [qty_dict.bal_qty] if wh.name in warehouse else [0.00]
|
row += [qty_dict.bal_qty] if wh.name == warehouse else [0.00]
|
||||||
total_stock_value += qty_dict.bal_val if wh.name in warehouse else 0.00
|
total_stock_value += qty_dict.bal_val if wh.name == warehouse else 0.00
|
||||||
|
|
||||||
item_balance[(item, item_map[item]["item_group"])].append(row)
|
item_balance[(item, item_map[item]["item_group"])].append(row)
|
||||||
item_value.setdefault((item, item_map[item]["item_group"]),[])
|
item_value.setdefault((item, item_map[item]["item_group"]),[])
|
||||||
|
|||||||
@@ -7,5 +7,4 @@ plaid-python~=7.2.1
|
|||||||
PyGithub==1.44.1
|
PyGithub==1.44.1
|
||||||
python-stdnum==1.12
|
python-stdnum==1.12
|
||||||
Unidecode==1.1.1
|
Unidecode==1.1.1
|
||||||
WooCommerce==2.1.1
|
|
||||||
pycryptodome==3.9.8
|
pycryptodome==3.9.8
|
||||||
|
|||||||
Reference in New Issue
Block a user