Merge branch 'develop' into separate-discount-account
This commit is contained in:
commit
78c8e46511
31
.github/workflows/release.yml
vendored
Normal file
31
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
name: Generate Semantic Release
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- version-13
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Entire Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Setup Node.js v14
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 14
|
||||||
|
- name: Setup dependencies
|
||||||
|
run: |
|
||||||
|
npm install @semantic-release/git @semantic-release/exec --no-save
|
||||||
|
- name: Create Release
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
GIT_AUTHOR_NAME: "Frappe PR Bot"
|
||||||
|
GIT_AUTHOR_EMAIL: "developers@frappe.io"
|
||||||
|
GIT_COMMITTER_NAME: "Frappe PR Bot"
|
||||||
|
GIT_COMMITTER_EMAIL: "developers@frappe.io"
|
||||||
|
run: npx semantic-release
|
24
.releaserc
Normal file
24
.releaserc
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"branches": ["version-13"],
|
||||||
|
"plugins": [
|
||||||
|
"@semantic-release/commit-analyzer", {
|
||||||
|
"preset": "angular",
|
||||||
|
"releaseRules": [
|
||||||
|
{"breaking": true, "release": false}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@semantic-release/release-notes-generator",
|
||||||
|
[
|
||||||
|
"@semantic-release/exec", {
|
||||||
|
"prepareCmd": 'sed -ir "s/[0-9]*\.[0-9]*\.[0-9]*/${nextRelease.version}/" erpnext/__init__.py'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"@semantic-release/git", {
|
||||||
|
"assets": ["erpnext/__init__.py"],
|
||||||
|
"message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@semantic-release/github"
|
||||||
|
]
|
||||||
|
}
|
@ -24,17 +24,16 @@ frappe.ui.form.on("E Commerce Settings", {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
frappe.model.with_doctype("Item", () => {
|
frappe.model.with_doctype("Website Item", () => {
|
||||||
const web_item_meta = frappe.get_meta('Website Item');
|
const web_item_meta = frappe.get_meta('Website Item');
|
||||||
|
|
||||||
const valid_fields = web_item_meta.fields.filter(
|
const valid_fields = web_item_meta.fields.filter(df =>
|
||||||
df => ["Link", "Table MultiSelect"].includes(df.fieldtype) && !df.hidden
|
["Link", "Table MultiSelect"].includes(df.fieldtype) && !df.hidden
|
||||||
).map(df => ({ label: df.label, value: df.fieldname }));
|
).map(df =>
|
||||||
|
({ label: df.label, value: df.fieldname })
|
||||||
frm.fields_dict.filter_fields.grid.update_docfield_property(
|
|
||||||
'fieldname', 'fieldtype', 'Select'
|
|
||||||
);
|
);
|
||||||
frm.fields_dict.filter_fields.grid.update_docfield_property(
|
|
||||||
|
frm.get_field("filter_fields").grid.update_docfield_property(
|
||||||
'fieldname', 'options', valid_fields
|
'fieldname', 'options', valid_fields
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -27,7 +27,7 @@ class ECommerceSettings(Document):
|
|||||||
self.is_redisearch_loaded = is_search_module_loaded()
|
self.is_redisearch_loaded = is_search_module_loaded()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_field_filters()
|
self.validate_field_filters(self.filter_fields, self.enable_field_filters)
|
||||||
self.validate_attribute_filters()
|
self.validate_attribute_filters()
|
||||||
self.validate_checkout()
|
self.validate_checkout()
|
||||||
self.validate_search_index_fields()
|
self.validate_search_index_fields()
|
||||||
@ -51,21 +51,22 @@ class ECommerceSettings(Document):
|
|||||||
define_autocomplete_dictionary()
|
define_autocomplete_dictionary()
|
||||||
create_website_items_index()
|
create_website_items_index()
|
||||||
|
|
||||||
def validate_field_filters(self):
|
@staticmethod
|
||||||
if not (self.enable_field_filters and self.filter_fields):
|
def validate_field_filters(filter_fields, enable_field_filters):
|
||||||
|
if not (enable_field_filters and filter_fields):
|
||||||
return
|
return
|
||||||
|
|
||||||
item_meta = frappe.get_meta("Item")
|
web_item_meta = frappe.get_meta("Website Item")
|
||||||
valid_fields = [
|
valid_fields = [
|
||||||
df.fieldname for df in item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]
|
df.fieldname for df in web_item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]
|
||||||
]
|
]
|
||||||
|
|
||||||
for f in self.filter_fields:
|
for row in filter_fields:
|
||||||
if f.fieldname not in valid_fields:
|
if row.fieldname not in valid_fields:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
"Filter Fields Row #{0}: Fieldname <b>{1}</b> must be of type 'Link' or 'Table MultiSelect'"
|
"Filter Fields Row #{0}: Fieldname {1} must be of type 'Link' or 'Table MultiSelect'"
|
||||||
).format(f.idx, f.fieldname)
|
).format(row.idx, frappe.bold(row.fieldname))
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_attribute_filters(self):
|
def validate_attribute_filters(self):
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
|
||||||
# See license.txt
|
# See license.txt
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
@ -11,44 +10,35 @@ from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import (
|
|||||||
|
|
||||||
|
|
||||||
class TestECommerceSettings(unittest.TestCase):
|
class TestECommerceSettings(unittest.TestCase):
|
||||||
def setUp(self):
|
def tearDown(self):
|
||||||
frappe.db.sql("""delete from `tabSingles` where doctype="Shipping Cart Settings" """)
|
frappe.db.rollback()
|
||||||
|
|
||||||
def get_cart_settings(self):
|
|
||||||
return frappe.get_doc({"doctype": "E Commerce Settings", "company": "_Test Company"})
|
|
||||||
|
|
||||||
# NOTE: Exchangrate API has all enabled currencies that ERPNext supports.
|
|
||||||
# We aren't checking just currency exchange record anymore
|
|
||||||
# while validating price list currency exchange rate to that of company.
|
|
||||||
# The API is being used to fetch the rate which again almost always
|
|
||||||
# gives back a valid value (for valid currencies).
|
|
||||||
# This makes the test obsolete.
|
|
||||||
# Commenting because im not sure if there's a better test we can write
|
|
||||||
|
|
||||||
# def test_exchange_rate_exists(self):
|
|
||||||
# frappe.db.sql("""delete from `tabCurrency Exchange`""")
|
|
||||||
|
|
||||||
# cart_settings = self.get_cart_settings()
|
|
||||||
# cart_settings.price_list = "_Test Price List Rest of the World"
|
|
||||||
# self.assertRaises(ShoppingCartSetupError, cart_settings.validate_exchange_rates_exist)
|
|
||||||
|
|
||||||
# from erpnext.setup.doctype.currency_exchange.test_currency_exchange import (
|
|
||||||
# test_records as currency_exchange_records,
|
|
||||||
# )
|
|
||||||
# frappe.get_doc(currency_exchange_records[0]).insert()
|
|
||||||
# cart_settings.validate_exchange_rates_exist()
|
|
||||||
|
|
||||||
def test_tax_rule_validation(self):
|
def test_tax_rule_validation(self):
|
||||||
frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0")
|
frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0")
|
||||||
frappe.db.commit() # nosemgrep
|
frappe.db.commit() # nosemgrep
|
||||||
|
|
||||||
cart_settings = self.get_cart_settings()
|
cart_settings = frappe.get_doc("E Commerce Settings")
|
||||||
cart_settings.enabled = 1
|
cart_settings.enabled = 1
|
||||||
if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1}, "name"):
|
if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1}, "name"):
|
||||||
self.assertRaises(ShoppingCartSetupError, cart_settings.validate_tax_rule)
|
self.assertRaises(ShoppingCartSetupError, cart_settings.validate_tax_rule)
|
||||||
|
|
||||||
frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 1")
|
frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 1")
|
||||||
|
|
||||||
|
def test_invalid_filter_fields(self):
|
||||||
|
"Check if Item fields are blocked in E Commerce Settings filter fields."
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
||||||
|
|
||||||
|
setup_e_commerce_settings({"enable_field_filters": 1})
|
||||||
|
|
||||||
|
create_custom_field(
|
||||||
|
"Item",
|
||||||
|
dict(owner="Administrator", fieldname="test_data", label="Test", fieldtype="Data"),
|
||||||
|
)
|
||||||
|
settings = frappe.get_doc("E Commerce Settings")
|
||||||
|
settings.append("filter_fields", {"fieldname": "test_data"})
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, settings.save)
|
||||||
|
|
||||||
|
|
||||||
def setup_e_commerce_settings(values_dict):
|
def setup_e_commerce_settings(values_dict):
|
||||||
"Accepts a dict of values that updates E Commerce Settings."
|
"Accepts a dict of values that updates E Commerce Settings."
|
||||||
|
@ -22,12 +22,14 @@ class ProductFiltersBuilder:
|
|||||||
fields, filter_data = [], []
|
fields, filter_data = [], []
|
||||||
filter_fields = [row.fieldname for row in self.doc.filter_fields] # fields in settings
|
filter_fields = [row.fieldname for row in self.doc.filter_fields] # fields in settings
|
||||||
|
|
||||||
# filter valid field filters i.e. those that exist in Item
|
# filter valid field filters i.e. those that exist in Website Item
|
||||||
item_meta = frappe.get_meta("Item", cached=True)
|
web_item_meta = frappe.get_meta("Website Item", cached=True)
|
||||||
fields = [item_meta.get_field(field) for field in filter_fields if item_meta.has_field(field)]
|
fields = [
|
||||||
|
web_item_meta.get_field(field) for field in filter_fields if web_item_meta.has_field(field)
|
||||||
|
]
|
||||||
|
|
||||||
for df in fields:
|
for df in fields:
|
||||||
item_filters, item_or_filters = {"published_in_website": 1}, []
|
item_filters, item_or_filters = {"published": 1}, []
|
||||||
link_doctype_values = self.get_filtered_link_doctype_records(df)
|
link_doctype_values = self.get_filtered_link_doctype_records(df)
|
||||||
|
|
||||||
if df.fieldtype == "Link":
|
if df.fieldtype == "Link":
|
||||||
@ -50,9 +52,13 @@ class ProductFiltersBuilder:
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# exclude variants if mentioned in settings
|
||||||
|
if frappe.db.get_single_value("E Commerce Settings", "hide_variants"):
|
||||||
|
item_filters["variant_of"] = ["is", "not set"]
|
||||||
|
|
||||||
# Get link field values attached to published items
|
# Get link field values attached to published items
|
||||||
item_values = frappe.get_all(
|
item_values = frappe.get_all(
|
||||||
"Item",
|
"Website Item",
|
||||||
fields=[df.fieldname],
|
fields=[df.fieldname],
|
||||||
filters=item_filters,
|
filters=item_filters,
|
||||||
or_filters=item_or_filters,
|
or_filters=item_or_filters,
|
||||||
|
@ -277,6 +277,54 @@ class TestProductDataEngine(unittest.TestCase):
|
|||||||
# tear down
|
# tear down
|
||||||
setup_e_commerce_settings({"enable_attribute_filters": 1, "hide_variants": 0})
|
setup_e_commerce_settings({"enable_attribute_filters": 1, "hide_variants": 0})
|
||||||
|
|
||||||
|
def test_custom_field_as_filter(self):
|
||||||
|
"Test if custom field functions as filter correctly."
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
||||||
|
|
||||||
|
create_custom_field(
|
||||||
|
"Website Item",
|
||||||
|
dict(
|
||||||
|
owner="Administrator",
|
||||||
|
fieldname="supplier",
|
||||||
|
label="Supplier",
|
||||||
|
fieldtype="Link",
|
||||||
|
options="Supplier",
|
||||||
|
insert_after="on_backorder",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Website Item", {"item_code": "Test 11I Laptop"}, "supplier", "_Test Supplier"
|
||||||
|
)
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Website Item", {"item_code": "Test 12I Laptop"}, "supplier", "_Test Supplier 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
settings = frappe.get_doc("E Commerce Settings")
|
||||||
|
settings.append("filter_fields", {"fieldname": "supplier"})
|
||||||
|
settings.save()
|
||||||
|
|
||||||
|
filter_engine = ProductFiltersBuilder()
|
||||||
|
field_filters = filter_engine.get_field_filters()
|
||||||
|
custom_filter = field_filters[1]
|
||||||
|
filter_values = custom_filter[1]
|
||||||
|
|
||||||
|
self.assertEqual(custom_filter[0].options, "Supplier")
|
||||||
|
self.assertEqual(len(filter_values), 2)
|
||||||
|
self.assertIn("_Test Supplier", filter_values)
|
||||||
|
|
||||||
|
# test if custom filter works in query
|
||||||
|
field_filters = {"supplier": "_Test Supplier 1"}
|
||||||
|
engine = ProductQuery()
|
||||||
|
result = engine.query(
|
||||||
|
attributes={}, fields=field_filters, search_term=None, start=0, item_group=None
|
||||||
|
)
|
||||||
|
items = result.get("items")
|
||||||
|
|
||||||
|
# check if only 'Raw Material' are fetched in the right order
|
||||||
|
self.assertEqual(len(items), 1)
|
||||||
|
self.assertEqual(items[0].get("item_code"), "Test 12I Laptop")
|
||||||
|
|
||||||
|
|
||||||
def create_variant_web_item():
|
def create_variant_web_item():
|
||||||
"Create Variant and Template Website Items."
|
"Create Variant and Template Website Items."
|
||||||
|
@ -153,6 +153,31 @@ class TestEmployeeCheckin(FrappeTestCase):
|
|||||||
log = make_checkin(employee, timestamp)
|
log = make_checkin(employee, timestamp)
|
||||||
self.assertIsNone(log.shift)
|
self.assertIsNone(log.shift)
|
||||||
|
|
||||||
|
def test_fetch_shift_for_assignment_with_end_date(self):
|
||||||
|
employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
|
||||||
|
|
||||||
|
# shift setup for 8-12
|
||||||
|
shift1 = setup_shift_type()
|
||||||
|
# 12:30 - 16:30
|
||||||
|
shift2 = setup_shift_type(shift_type="Shift 2", start_time="12:30:00", end_time="16:30:00")
|
||||||
|
|
||||||
|
date = getdate()
|
||||||
|
make_shift_assignment(shift1.name, employee, date, add_days(date, 15))
|
||||||
|
make_shift_assignment(shift2.name, employee, date, add_days(date, 15))
|
||||||
|
|
||||||
|
timestamp = datetime.combine(date, get_time("08:45:00"))
|
||||||
|
log = make_checkin(employee, timestamp)
|
||||||
|
self.assertEqual(log.shift, shift1.name)
|
||||||
|
|
||||||
|
timestamp = datetime.combine(date, get_time("12:45:00"))
|
||||||
|
log = make_checkin(employee, timestamp)
|
||||||
|
self.assertEqual(log.shift, shift2.name)
|
||||||
|
|
||||||
|
# log after end date
|
||||||
|
timestamp = datetime.combine(add_days(date, 16), get_time("12:45:00"))
|
||||||
|
log = make_checkin(employee, timestamp)
|
||||||
|
self.assertIsNone(log.shift)
|
||||||
|
|
||||||
def test_shift_start_and_end_timings(self):
|
def test_shift_start_and_end_timings(self):
|
||||||
employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
|
employee = make_employee("test_employee_checkin@example.com", company="_Test Company")
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ def has_overlapping_timings(shift_1: str, shift_2: str) -> bool:
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_events(start, end, filters=None):
|
def get_events(start, end, filters=None):
|
||||||
events = []
|
from frappe.desk.calendar import get_event_conditions
|
||||||
|
|
||||||
employee = frappe.db.get_value(
|
employee = frappe.db.get_value(
|
||||||
"Employee", {"user_id": frappe.session.user}, ["name", "company"], as_dict=True
|
"Employee", {"user_id": frappe.session.user}, ["name", "company"], as_dict=True
|
||||||
@ -132,20 +132,22 @@ def get_events(start, end, filters=None):
|
|||||||
employee = ""
|
employee = ""
|
||||||
company = frappe.db.get_value("Global Defaults", None, "default_company")
|
company = frappe.db.get_value("Global Defaults", None, "default_company")
|
||||||
|
|
||||||
from frappe.desk.reportview import get_filters_cond
|
conditions = get_event_conditions("Shift Assignment", filters)
|
||||||
|
events = add_assignments(start, end, conditions=conditions)
|
||||||
conditions = get_filters_cond("Shift Assignment", filters, [])
|
|
||||||
add_assignments(events, start, end, conditions=conditions)
|
|
||||||
return events
|
return events
|
||||||
|
|
||||||
|
|
||||||
def add_assignments(events, start, end, conditions=None):
|
def add_assignments(start, end, conditions=None):
|
||||||
|
events = []
|
||||||
|
|
||||||
query = """select name, start_date, end_date, employee_name,
|
query = """select name, start_date, end_date, employee_name,
|
||||||
employee, docstatus, shift_type
|
employee, docstatus, shift_type
|
||||||
from `tabShift Assignment` where
|
from `tabShift Assignment` where
|
||||||
start_date >= %(start_date)s
|
(
|
||||||
or end_date <= %(end_date)s
|
start_date >= %(start_date)s
|
||||||
or (%(start_date)s between start_date and end_date and %(end_date)s between start_date and end_date)
|
or end_date <= %(end_date)s
|
||||||
|
or (%(start_date)s between start_date and end_date and %(end_date)s between start_date and end_date)
|
||||||
|
)
|
||||||
and docstatus = 1"""
|
and docstatus = 1"""
|
||||||
if conditions:
|
if conditions:
|
||||||
query += conditions
|
query += conditions
|
||||||
@ -251,7 +253,7 @@ def get_shifts_for_date(employee: str, for_timestamp: datetime) -> List[Dict[str
|
|||||||
Criterion.any(
|
Criterion.any(
|
||||||
[
|
[
|
||||||
assignment.end_date.isnull(),
|
assignment.end_date.isnull(),
|
||||||
(assignment.end_date.isnotnull() & (getdate(for_timestamp.date()) >= assignment.end_date)),
|
(assignment.end_date.isnotnull() & (getdate(for_timestamp.date()) <= assignment.end_date)),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -8,7 +8,7 @@ from frappe.tests.utils import FrappeTestCase
|
|||||||
from frappe.utils import add_days, getdate, nowdate
|
from frappe.utils import add_days, getdate, nowdate
|
||||||
|
|
||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
from erpnext.hr.doctype.shift_assignment.shift_assignment import OverlappingShiftError
|
from erpnext.hr.doctype.shift_assignment.shift_assignment import OverlappingShiftError, get_events
|
||||||
from erpnext.hr.doctype.shift_type.test_shift_type import make_shift_assignment, setup_shift_type
|
from erpnext.hr.doctype.shift_type.test_shift_type import make_shift_assignment, setup_shift_type
|
||||||
|
|
||||||
test_dependencies = ["Shift Type"]
|
test_dependencies = ["Shift Type"]
|
||||||
@ -154,3 +154,18 @@ class TestShiftAssignment(FrappeTestCase):
|
|||||||
shift_type = setup_shift_type(shift_type="Shift 2", start_time="13:00:00", end_time="15:00:00")
|
shift_type = setup_shift_type(shift_type="Shift 2", start_time="13:00:00", end_time="15:00:00")
|
||||||
date = getdate()
|
date = getdate()
|
||||||
make_shift_assignment(shift_type.name, employee, date)
|
make_shift_assignment(shift_type.name, employee, date)
|
||||||
|
|
||||||
|
def test_shift_assignment_calendar(self):
|
||||||
|
employee1 = make_employee("test_shift_assignment1@example.com", company="_Test Company")
|
||||||
|
employee2 = make_employee("test_shift_assignment2@example.com", company="_Test Company")
|
||||||
|
|
||||||
|
shift_type = setup_shift_type(shift_type="Shift 1", start_time="08:00:00", end_time="12:00:00")
|
||||||
|
date = getdate()
|
||||||
|
shift1 = make_shift_assignment(shift_type.name, employee1, date)
|
||||||
|
make_shift_assignment(shift_type.name, employee2, date)
|
||||||
|
|
||||||
|
events = get_events(
|
||||||
|
start=date, end=date, filters=[["Shift Assignment", "employee", "=", employee1, False]]
|
||||||
|
)
|
||||||
|
self.assertEqual(len(events), 1)
|
||||||
|
self.assertEqual(events[0]["name"], shift1.name)
|
||||||
|
@ -73,10 +73,22 @@ frappe.ui.form.on('Job Card', {
|
|||||||
if (frm.doc.docstatus == 0 && !frm.is_new() &&
|
if (frm.doc.docstatus == 0 && !frm.is_new() &&
|
||||||
(frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity)
|
(frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity)
|
||||||
&& (frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
|
&& (frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
|
||||||
frm.trigger("prepare_timer_buttons");
|
|
||||||
|
// if Job Card is link to Work Order, the job card must not be able to start if Work Order not "Started"
|
||||||
|
// and if stock mvt for WIP is required
|
||||||
|
if (frm.doc.work_order) {
|
||||||
|
frappe.db.get_value('Work Order', frm.doc.work_order, ['skip_transfer', 'status'], (result) => {
|
||||||
|
if (result.skip_transfer === 1 || result.status == 'In Process') {
|
||||||
|
frm.trigger("prepare_timer_buttons");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frm.trigger("prepare_timer_buttons");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.trigger("setup_quality_inspection");
|
frm.trigger("setup_quality_inspection");
|
||||||
|
|
||||||
if (frm.doc.work_order) {
|
if (frm.doc.work_order) {
|
||||||
frappe.db.get_value('Work Order', frm.doc.work_order,
|
frappe.db.get_value('Work Order', frm.doc.work_order,
|
||||||
'transfer_material_against').then((r) => {
|
'transfer_material_against').then((r) => {
|
||||||
|
@ -364,4 +364,5 @@ erpnext.patches.v13_0.set_return_against_in_pos_invoice_references
|
|||||||
erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items # 24-03-2022
|
erpnext.patches.v13_0.remove_unknown_links_to_prod_plan_items # 24-03-2022
|
||||||
erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances
|
erpnext.patches.v13_0.update_expense_claim_status_for_paid_advances
|
||||||
erpnext.patches.v13_0.create_gst_custom_fields_in_quotation
|
erpnext.patches.v13_0.create_gst_custom_fields_in_quotation
|
||||||
|
erpnext.patches.v13_0.copy_custom_field_filters_to_website_item
|
||||||
erpnext.patches.v14_0.discount_accounting_separation
|
erpnext.patches.v14_0.discount_accounting_separation
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
"Add Field Filters, that are not standard fields in Website Item, as Custom Fields."
|
||||||
|
|
||||||
|
def move_table_multiselect_data(docfield):
|
||||||
|
"Copy child table data (Table Multiselect) from Item to Website Item for a docfield."
|
||||||
|
table_multiselect_data = get_table_multiselect_data(docfield)
|
||||||
|
field = docfield.fieldname
|
||||||
|
|
||||||
|
for row in table_multiselect_data:
|
||||||
|
# add copied multiselect data rows in Website Item
|
||||||
|
web_item = frappe.db.get_value("Website Item", {"item_code": row.parent})
|
||||||
|
web_item_doc = frappe.get_doc("Website Item", web_item)
|
||||||
|
|
||||||
|
child_doc = frappe.new_doc(docfield.options, web_item_doc, field)
|
||||||
|
|
||||||
|
for field in ["name", "creation", "modified", "idx"]:
|
||||||
|
row[field] = None
|
||||||
|
|
||||||
|
child_doc.update(row)
|
||||||
|
|
||||||
|
child_doc.parenttype = "Website Item"
|
||||||
|
child_doc.parent = web_item
|
||||||
|
|
||||||
|
child_doc.insert()
|
||||||
|
|
||||||
|
def get_table_multiselect_data(docfield):
|
||||||
|
child_table = frappe.qb.DocType(docfield.options)
|
||||||
|
item = frappe.qb.DocType("Item")
|
||||||
|
|
||||||
|
table_multiselect_data = ( # query table data for field
|
||||||
|
frappe.qb.from_(child_table)
|
||||||
|
.join(item)
|
||||||
|
.on(item.item_code == child_table.parent)
|
||||||
|
.select(child_table.star)
|
||||||
|
.where((child_table.parentfield == docfield.fieldname) & (item.published_in_website == 1))
|
||||||
|
).run(as_dict=True)
|
||||||
|
|
||||||
|
return table_multiselect_data
|
||||||
|
|
||||||
|
settings = frappe.get_doc("E Commerce Settings")
|
||||||
|
|
||||||
|
if not (settings.enable_field_filters or settings.filter_fields):
|
||||||
|
return
|
||||||
|
|
||||||
|
item_meta = frappe.get_meta("Item")
|
||||||
|
valid_item_fields = [
|
||||||
|
df.fieldname for df in item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]
|
||||||
|
]
|
||||||
|
|
||||||
|
web_item_meta = frappe.get_meta("Website Item")
|
||||||
|
valid_web_item_fields = [
|
||||||
|
df.fieldname for df in web_item_meta.fields if df.fieldtype in ["Link", "Table MultiSelect"]
|
||||||
|
]
|
||||||
|
|
||||||
|
for row in settings.filter_fields:
|
||||||
|
# skip if illegal field
|
||||||
|
if row.fieldname not in valid_item_fields:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# if Item field is not in Website Item, add it as a custom field
|
||||||
|
if row.fieldname not in valid_web_item_fields:
|
||||||
|
df = item_meta.get_field(row.fieldname)
|
||||||
|
create_custom_field(
|
||||||
|
"Website Item",
|
||||||
|
dict(
|
||||||
|
owner="Administrator",
|
||||||
|
fieldname=df.fieldname,
|
||||||
|
label=df.label,
|
||||||
|
fieldtype=df.fieldtype,
|
||||||
|
options=df.options,
|
||||||
|
description=df.description,
|
||||||
|
read_only=df.read_only,
|
||||||
|
no_copy=df.no_copy,
|
||||||
|
insert_after="on_backorder",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# map field values
|
||||||
|
if df.fieldtype == "Table MultiSelect":
|
||||||
|
move_table_multiselect_data(df)
|
||||||
|
else:
|
||||||
|
frappe.db.sql( # nosemgrep
|
||||||
|
"""
|
||||||
|
UPDATE `tabWebsite Item` wi, `tabItem` i
|
||||||
|
SET wi.{0} = i.{0}
|
||||||
|
WHERE wi.item_code = i.item_code
|
||||||
|
""".format(
|
||||||
|
row.fieldname
|
||||||
|
)
|
||||||
|
)
|
@ -10,54 +10,58 @@ def execute():
|
|||||||
|
|
||||||
frappe.reload_doc("hr", "doctype", "Leave Encashment")
|
frappe.reload_doc("hr", "doctype", "Leave Encashment")
|
||||||
|
|
||||||
additional_salaries = frappe.get_all(
|
if frappe.db.has_column("Leave Encashment", "additional_salary"):
|
||||||
"Additional Salary",
|
leave_encashments = frappe.get_all(
|
||||||
fields=["name", "salary_slip", "type", "salary_component"],
|
"Leave Encashment",
|
||||||
filters={"salary_slip": ["!=", ""]},
|
fields=["name", "additional_salary"],
|
||||||
group_by="salary_slip",
|
filters={"additional_salary": ["!=", ""]},
|
||||||
)
|
|
||||||
leave_encashments = frappe.get_all(
|
|
||||||
"Leave Encashment",
|
|
||||||
fields=["name", "additional_salary"],
|
|
||||||
filters={"additional_salary": ["!=", ""]},
|
|
||||||
)
|
|
||||||
employee_incentives = frappe.get_all(
|
|
||||||
"Employee Incentive",
|
|
||||||
fields=["name", "additional_salary"],
|
|
||||||
filters={"additional_salary": ["!=", ""]},
|
|
||||||
)
|
|
||||||
|
|
||||||
for incentive in employee_incentives:
|
|
||||||
frappe.db.sql(
|
|
||||||
""" UPDATE `tabAdditional Salary`
|
|
||||||
SET ref_doctype = 'Employee Incentive', ref_docname = %s
|
|
||||||
WHERE name = %s
|
|
||||||
""",
|
|
||||||
(incentive["name"], incentive["additional_salary"]),
|
|
||||||
)
|
)
|
||||||
|
for leave_encashment in leave_encashments:
|
||||||
for leave_encashment in leave_encashments:
|
|
||||||
frappe.db.sql(
|
|
||||||
""" UPDATE `tabAdditional Salary`
|
|
||||||
SET ref_doctype = 'Leave Encashment', ref_docname = %s
|
|
||||||
WHERE name = %s
|
|
||||||
""",
|
|
||||||
(leave_encashment["name"], leave_encashment["additional_salary"]),
|
|
||||||
)
|
|
||||||
|
|
||||||
salary_slips = [sal["salary_slip"] for sal in additional_salaries]
|
|
||||||
|
|
||||||
for salary in additional_salaries:
|
|
||||||
comp_type = "earnings" if salary["type"] == "Earning" else "deductions"
|
|
||||||
if salary["salary_slip"] and salary_slips.count(salary["salary_slip"]) == 1:
|
|
||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
"""
|
""" UPDATE `tabAdditional Salary`
|
||||||
UPDATE `tabSalary Detail`
|
SET ref_doctype = 'Leave Encashment', ref_docname = %s
|
||||||
SET additional_salary = %s
|
WHERE name = %s
|
||||||
WHERE parenttype = 'Salary Slip'
|
|
||||||
and parentfield = %s
|
|
||||||
and parent = %s
|
|
||||||
and salary_component = %s
|
|
||||||
""",
|
""",
|
||||||
(salary["name"], comp_type, salary["salary_slip"], salary["salary_component"]),
|
(leave_encashment["name"], leave_encashment["additional_salary"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if frappe.db.has_column("Employee Incentive", "additional_salary"):
|
||||||
|
employee_incentives = frappe.get_all(
|
||||||
|
"Employee Incentive",
|
||||||
|
fields=["name", "additional_salary"],
|
||||||
|
filters={"additional_salary": ["!=", ""]},
|
||||||
|
)
|
||||||
|
|
||||||
|
for incentive in employee_incentives:
|
||||||
|
frappe.db.sql(
|
||||||
|
""" UPDATE `tabAdditional Salary`
|
||||||
|
SET ref_doctype = 'Employee Incentive', ref_docname = %s
|
||||||
|
WHERE name = %s
|
||||||
|
""",
|
||||||
|
(incentive["name"], incentive["additional_salary"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
if frappe.db.has_column("Additional Salary", "salary_slip"):
|
||||||
|
additional_salaries = frappe.get_all(
|
||||||
|
"Additional Salary",
|
||||||
|
fields=["name", "salary_slip", "type", "salary_component"],
|
||||||
|
filters={"salary_slip": ["!=", ""]},
|
||||||
|
group_by="salary_slip",
|
||||||
|
)
|
||||||
|
|
||||||
|
salary_slips = [sal["salary_slip"] for sal in additional_salaries]
|
||||||
|
|
||||||
|
for salary in additional_salaries:
|
||||||
|
comp_type = "earnings" if salary["type"] == "Earning" else "deductions"
|
||||||
|
if salary["salary_slip"] and salary_slips.count(salary["salary_slip"]) == 1:
|
||||||
|
frappe.db.sql(
|
||||||
|
"""
|
||||||
|
UPDATE `tabSalary Detail`
|
||||||
|
SET additional_salary = %s
|
||||||
|
WHERE parenttype = 'Salary Slip'
|
||||||
|
and parentfield = %s
|
||||||
|
and parent = %s
|
||||||
|
and salary_component = %s
|
||||||
|
""",
|
||||||
|
(salary["name"], comp_type, salary["salary_slip"], salary["salary_component"]),
|
||||||
|
)
|
||||||
|
@ -1,76 +1,31 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
"creation": "2018-12-31 17:06:08.716134",
|
||||||
"allow_guest_to_view": 0,
|
"doctype": "DocType",
|
||||||
"allow_import": 0,
|
"editable_grid": 1,
|
||||||
"allow_rename": 0,
|
"engine": "InnoDB",
|
||||||
"beta": 0,
|
"field_order": [
|
||||||
"creation": "2018-12-31 17:06:08.716134",
|
"fieldname"
|
||||||
"custom": 0,
|
],
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
|
||||||
"engine": "InnoDB",
|
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "fieldname",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Autocomplete",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Fieldname"
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "fieldname",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"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": "Fieldname",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"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
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"istable": 1,
|
||||||
"hide_heading": 0,
|
"links": [],
|
||||||
"hide_toolbar": 0,
|
"modified": "2022-04-18 18:55:17.835666",
|
||||||
"idx": 0,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Portal",
|
||||||
"in_create": 0,
|
"name": "Website Filter Field",
|
||||||
"is_submittable": 0,
|
"owner": "Administrator",
|
||||||
"issingle": 0,
|
"permissions": [],
|
||||||
"istable": 1,
|
"quick_entry": 1,
|
||||||
"max_attachments": 0,
|
"sort_field": "modified",
|
||||||
"modified": "2019-01-01 18:26:11.550380",
|
"sort_order": "DESC",
|
||||||
"modified_by": "Administrator",
|
"states": [],
|
||||||
"module": "Portal",
|
"track_changes": 1
|
||||||
"name": "Website Filter Field",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [],
|
|
||||||
"quick_entry": 1,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -1388,6 +1388,11 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Target doc created from a mapped doc
|
||||||
|
if (this.frm.doc.__onload && this.frm.doc.__onload.ignore_price_list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return this.frm.call({
|
return this.frm.call({
|
||||||
method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.apply_pricing_rule",
|
method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.apply_pricing_rule",
|
||||||
args: { args: args, doc: me.frm.doc },
|
args: { args: args, doc: me.frm.doc },
|
||||||
@ -1504,7 +1509,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
me.remove_pricing_rule(frappe.get_doc(d.doctype, d.name));
|
me.remove_pricing_rule(frappe.get_doc(d.doctype, d.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.free_item_data) {
|
if (d.free_item_data.length > 0) {
|
||||||
me.apply_product_discount(d);
|
me.apply_product_discount(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1074,7 +1074,7 @@ class GSPConnector:
|
|||||||
"Distance": cint(eway_bill_details.distance),
|
"Distance": cint(eway_bill_details.distance),
|
||||||
"TransMode": eway_bill_details.mode_of_transport,
|
"TransMode": eway_bill_details.mode_of_transport,
|
||||||
"TransId": eway_bill_details.gstin,
|
"TransId": eway_bill_details.gstin,
|
||||||
"TransName": eway_bill_details.transporter,
|
"TransName": eway_bill_details.name,
|
||||||
"TrnDocDt": eway_bill_details.document_date,
|
"TrnDocDt": eway_bill_details.document_date,
|
||||||
"TrnDocNo": eway_bill_details.document_name,
|
"TrnDocNo": eway_bill_details.document_name,
|
||||||
"VehNo": eway_bill_details.vehicle_no,
|
"VehNo": eway_bill_details.vehicle_no,
|
||||||
|
@ -81,7 +81,7 @@ def get_data(conditions, filters):
|
|||||||
ON sii.so_detail = soi.name and sii.docstatus = 1)
|
ON sii.so_detail = soi.name and sii.docstatus = 1)
|
||||||
LEFT JOIN `tabDelivery Note Item` dni
|
LEFT JOIN `tabDelivery Note Item` dni
|
||||||
on dni.so_detail = soi.name
|
on dni.so_detail = soi.name
|
||||||
RIGHT JOIN `tabDelivery Note` dn
|
LEFT JOIN `tabDelivery Note` dn
|
||||||
on dni.parent = dn.name and dn.docstatus = 1
|
on dni.parent = dn.name and dn.docstatus = 1
|
||||||
WHERE
|
WHERE
|
||||||
soi.parent = so.name
|
soi.parent = so.name
|
||||||
|
@ -0,0 +1,166 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
from frappe.utils import add_days
|
||||||
|
|
||||||
|
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice
|
||||||
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
|
from erpnext.selling.report.sales_order_analysis.sales_order_analysis import execute
|
||||||
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
|
|
||||||
|
test_dependencies = ["Sales Order", "Item", "Sales Invoice", "Delivery Note"]
|
||||||
|
|
||||||
|
|
||||||
|
class TestSalesOrderAnalysis(FrappeTestCase):
|
||||||
|
def create_sales_order(self, transaction_date):
|
||||||
|
item = create_item(item_code="_Test Excavator", is_stock_item=0)
|
||||||
|
so = make_sales_order(
|
||||||
|
transaction_date=transaction_date,
|
||||||
|
item=item.item_code,
|
||||||
|
qty=10,
|
||||||
|
rate=100000,
|
||||||
|
do_not_save=True,
|
||||||
|
)
|
||||||
|
so.po_no = ""
|
||||||
|
so.taxes_and_charges = ""
|
||||||
|
so.taxes = ""
|
||||||
|
so.items[0].delivery_date = add_days(transaction_date, 15)
|
||||||
|
so.save()
|
||||||
|
so.submit()
|
||||||
|
return item, so
|
||||||
|
|
||||||
|
def create_sales_invoice(self, so):
|
||||||
|
sinv = make_sales_invoice(so.name)
|
||||||
|
sinv.posting_date = so.transaction_date
|
||||||
|
sinv.taxes_and_charges = ""
|
||||||
|
sinv.taxes = ""
|
||||||
|
sinv.insert()
|
||||||
|
sinv.submit()
|
||||||
|
return sinv
|
||||||
|
|
||||||
|
def create_delivery_note(self, so):
|
||||||
|
dn = make_delivery_note(so.name)
|
||||||
|
dn.set_posting_time = True
|
||||||
|
dn.posting_date = add_days(so.transaction_date, 1)
|
||||||
|
dn.save()
|
||||||
|
dn.submit()
|
||||||
|
return dn
|
||||||
|
|
||||||
|
def test_01_so_to_deliver_and_bill(self):
|
||||||
|
transaction_date = "2021-06-01"
|
||||||
|
item, so = self.create_sales_order(transaction_date)
|
||||||
|
columns, data, message, chart = execute(
|
||||||
|
{
|
||||||
|
"company": "_Test Company",
|
||||||
|
"from_date": "2021-06-01",
|
||||||
|
"to_date": "2021-06-30",
|
||||||
|
"status": ["To Deliver and Bill"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expected_value = {
|
||||||
|
"status": "To Deliver and Bill",
|
||||||
|
"sales_order": so.name,
|
||||||
|
"delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date),
|
||||||
|
"qty": 10,
|
||||||
|
"delivered_qty": 0,
|
||||||
|
"pending_qty": 10,
|
||||||
|
"qty_to_bill": 10,
|
||||||
|
"time_taken_to_deliver": 0,
|
||||||
|
}
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
for key, val in expected_value.items():
|
||||||
|
with self.subTest(key=key, val=val):
|
||||||
|
self.assertEqual(data[0][key], val)
|
||||||
|
|
||||||
|
def test_02_so_to_deliver(self):
|
||||||
|
transaction_date = "2021-06-01"
|
||||||
|
item, so = self.create_sales_order(transaction_date)
|
||||||
|
self.create_sales_invoice(so)
|
||||||
|
columns, data, message, chart = execute(
|
||||||
|
{
|
||||||
|
"company": "_Test Company",
|
||||||
|
"from_date": "2021-06-01",
|
||||||
|
"to_date": "2021-06-30",
|
||||||
|
"status": ["To Deliver"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expected_value = {
|
||||||
|
"status": "To Deliver",
|
||||||
|
"sales_order": so.name,
|
||||||
|
"delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date),
|
||||||
|
"qty": 10,
|
||||||
|
"delivered_qty": 0,
|
||||||
|
"pending_qty": 10,
|
||||||
|
"qty_to_bill": 0,
|
||||||
|
"time_taken_to_deliver": 0,
|
||||||
|
}
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
for key, val in expected_value.items():
|
||||||
|
with self.subTest(key=key, val=val):
|
||||||
|
self.assertEqual(data[0][key], val)
|
||||||
|
|
||||||
|
def test_03_so_to_bill(self):
|
||||||
|
transaction_date = "2021-06-01"
|
||||||
|
item, so = self.create_sales_order(transaction_date)
|
||||||
|
self.create_delivery_note(so)
|
||||||
|
columns, data, message, chart = execute(
|
||||||
|
{
|
||||||
|
"company": "_Test Company",
|
||||||
|
"from_date": "2021-06-01",
|
||||||
|
"to_date": "2021-06-30",
|
||||||
|
"status": ["To Bill"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expected_value = {
|
||||||
|
"status": "To Bill",
|
||||||
|
"sales_order": so.name,
|
||||||
|
"delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date),
|
||||||
|
"qty": 10,
|
||||||
|
"delivered_qty": 10,
|
||||||
|
"pending_qty": 0,
|
||||||
|
"qty_to_bill": 10,
|
||||||
|
"time_taken_to_deliver": 86400,
|
||||||
|
}
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
for key, val in expected_value.items():
|
||||||
|
with self.subTest(key=key, val=val):
|
||||||
|
self.assertEqual(data[0][key], val)
|
||||||
|
|
||||||
|
def test_04_so_completed(self):
|
||||||
|
transaction_date = "2021-06-01"
|
||||||
|
item, so = self.create_sales_order(transaction_date)
|
||||||
|
self.create_sales_invoice(so)
|
||||||
|
self.create_delivery_note(so)
|
||||||
|
columns, data, message, chart = execute(
|
||||||
|
{
|
||||||
|
"company": "_Test Company",
|
||||||
|
"from_date": "2021-06-01",
|
||||||
|
"to_date": "2021-06-30",
|
||||||
|
"status": ["Completed"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expected_value = {
|
||||||
|
"status": "Completed",
|
||||||
|
"sales_order": so.name,
|
||||||
|
"delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date),
|
||||||
|
"qty": 10,
|
||||||
|
"delivered_qty": 10,
|
||||||
|
"pending_qty": 0,
|
||||||
|
"qty_to_bill": 0,
|
||||||
|
"billed_qty": 10,
|
||||||
|
"time_taken_to_deliver": 86400,
|
||||||
|
}
|
||||||
|
self.assertEqual(len(data), 1)
|
||||||
|
for key, val in expected_value.items():
|
||||||
|
with self.subTest(key=key, val=val):
|
||||||
|
self.assertEqual(data[0][key], val)
|
||||||
|
|
||||||
|
def test_05_all_so_status(self):
|
||||||
|
columns, data, message, chart = execute(
|
||||||
|
{
|
||||||
|
"company": "_Test Company",
|
||||||
|
"from_date": "2021-06-01",
|
||||||
|
"to_date": "2021-06-30",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# SO's from first 4 test cases should be in output
|
||||||
|
self.assertEqual(len(data), 4)
|
@ -72,17 +72,16 @@ frappe.ui.form.on("Item Group", {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
frappe.model.with_doctype('Item', () => {
|
frappe.model.with_doctype('Website Item', () => {
|
||||||
const item_meta = frappe.get_meta('Item');
|
const web_item_meta = frappe.get_meta('Website Item');
|
||||||
|
|
||||||
const valid_fields = item_meta.fields.filter(
|
const valid_fields = web_item_meta.fields.filter(df =>
|
||||||
df => ['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
|
['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
|
||||||
).map(df => ({ label: df.label, value: df.fieldname }));
|
).map(df =>
|
||||||
|
({ label: df.label, value: df.fieldname })
|
||||||
frm.fields_dict.filter_fields.grid.update_docfield_property(
|
|
||||||
'fieldname', 'fieldtype', 'Select'
|
|
||||||
);
|
);
|
||||||
frm.fields_dict.filter_fields.grid.update_docfield_property(
|
|
||||||
|
frm.get_field("filter_fields").grid.update_docfield_property(
|
||||||
'fieldname', 'options', valid_fields
|
'fieldname', 'options', valid_fields
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -11,6 +11,7 @@ from frappe.utils.nestedset import NestedSet
|
|||||||
from frappe.website.utils import clear_cache
|
from frappe.website.utils import clear_cache
|
||||||
from frappe.website.website_generator import WebsiteGenerator
|
from frappe.website.website_generator import WebsiteGenerator
|
||||||
|
|
||||||
|
from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import ECommerceSettings
|
||||||
from erpnext.e_commerce.product_data_engine.filters import ProductFiltersBuilder
|
from erpnext.e_commerce.product_data_engine.filters import ProductFiltersBuilder
|
||||||
|
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ class ItemGroup(NestedSet, WebsiteGenerator):
|
|||||||
|
|
||||||
self.make_route()
|
self.make_route()
|
||||||
self.validate_item_group_defaults()
|
self.validate_item_group_defaults()
|
||||||
|
ECommerceSettings.validate_field_filters(self.filter_fields, enable_field_filters=True)
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
NestedSet.on_update(self)
|
NestedSet.on_update(self)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user