Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/frappe/erpnext into roun…
Browse files Browse the repository at this point in the history
…d_off_account_cost_center
  • Loading branch information
deepeshgarg007 committed Apr 23, 2022
2 parents 015812b + deed970 commit 783793d
Show file tree
Hide file tree
Showing 41 changed files with 1,488 additions and 1,462 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ jobs:
uses: actions/checkout@v2
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js v14
uses: actions/setup-node@v2
with:
Expand All @@ -21,5 +22,10 @@ jobs:
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
28 changes: 27 additions & 1 deletion erpnext/accounts/test/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import unittest

import frappe
from frappe.test_runner import make_test_objects

from erpnext.accounts.party import get_party_shipping_address
from erpnext.accounts.utils import get_future_stock_vouchers, get_voucherwise_gl_entries
from erpnext.accounts.utils import (
get_future_stock_vouchers,
get_voucherwise_gl_entries,
sort_stock_vouchers_by_posting_date,
)
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry


class TestUtils(unittest.TestCase):
Expand Down Expand Up @@ -47,6 +54,25 @@ def test_get_voucher_wise_gl_entry(self):
msg="get_voucherwise_gl_entries not returning expected GLes",
)

def test_stock_voucher_sorting(self):
vouchers = []

item = make_item().name

stock_entry = {"item": item, "to_warehouse": "_Test Warehouse - _TC", "qty": 1, "rate": 10}

se1 = make_stock_entry(posting_date="2022-01-01", **stock_entry)
se2 = make_stock_entry(posting_date="2022-02-01", **stock_entry)
se3 = make_stock_entry(posting_date="2022-03-01", **stock_entry)

for doc in (se1, se2, se3):
vouchers.append((doc.doctype, doc.name))

vouchers.append(("Stock Entry", "Wat"))

sorted_vouchers = sort_stock_vouchers_by_posting_date(list(reversed(vouchers)))
self.assertEqual(sorted_vouchers, vouchers)


ADDRESS_RECORDS = [
{
Expand Down
27 changes: 27 additions & 0 deletions erpnext/accounts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@


from json import loads
from typing import List, Tuple

import frappe
import frappe.defaults
Expand Down Expand Up @@ -1122,13 +1123,18 @@ def update_gl_entries_after(
def repost_gle_for_stock_vouchers(
stock_vouchers, posting_date, company=None, warehouse_account=None
):
if not stock_vouchers:
return

def _delete_gl_entries(voucher_type, voucher_no):
frappe.db.sql(
"""delete from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""",
(voucher_type, voucher_no),
)

stock_vouchers = sort_stock_vouchers_by_posting_date(stock_vouchers)

if not warehouse_account:
warehouse_account = get_warehouse_account_map(company)

Expand All @@ -1149,6 +1155,27 @@ def _delete_gl_entries(voucher_type, voucher_no):
_delete_gl_entries(voucher_type, voucher_no)


def sort_stock_vouchers_by_posting_date(
stock_vouchers: List[Tuple[str, str]]
) -> List[Tuple[str, str]]:
sle = frappe.qb.DocType("Stock Ledger Entry")
voucher_nos = [v[1] for v in stock_vouchers]

sles = (
frappe.qb.from_(sle)
.select(sle.voucher_type, sle.voucher_no, sle.posting_date, sle.posting_time, sle.creation)
.where((sle.is_cancelled == 0) & (sle.voucher_no.isin(voucher_nos)))
.groupby(sle.voucher_type, sle.voucher_no)
).run(as_dict=True)
sorted_vouchers = [(sle.voucher_type, sle.voucher_no) for sle in sles]

unknown_vouchers = set(stock_vouchers) - set(sorted_vouchers)
if unknown_vouchers:
sorted_vouchers.extend(unknown_vouchers)

return sorted_vouchers


def get_future_stock_vouchers(
posting_date, posting_time, for_warehouses=None, for_items=None, company=None
):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 valid_fields = web_item_meta.fields.filter(
df => ["Link", "Table MultiSelect"].includes(df.fieldtype) && !df.hidden
).map(df => ({ label: df.label, value: df.fieldname }));

frm.fields_dict.filter_fields.grid.update_docfield_property(
'fieldname', 'fieldtype', 'Select'
const valid_fields = web_item_meta.fields.filter(df =>
["Link", "Table MultiSelect"].includes(df.fieldtype) && !df.hidden
).map(df =>
({ label: df.label, value: df.fieldname })
);
frm.fields_dict.filter_fields.grid.update_docfield_property(

frm.get_field("filter_fields").grid.update_docfield_property(
'fieldname', 'options', valid_fields
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def onload(self):
self.is_redisearch_loaded = is_search_module_loaded()

def validate(self):
self.validate_field_filters()
self.validate_field_filters(self.filter_fields, self.enable_field_filters)
self.validate_attribute_filters()
self.validate_checkout()
self.validate_search_index_fields()
Expand All @@ -51,21 +51,22 @@ def create_redisearch_indexes(self):
define_autocomplete_dictionary()
create_website_items_index()

def validate_field_filters(self):
if not (self.enable_field_filters and self.filter_fields):
@staticmethod
def validate_field_filters(filter_fields, enable_field_filters):
if not (enable_field_filters and filter_fields):
return

item_meta = frappe.get_meta("Item")
web_item_meta = frappe.get_meta("Website Item")
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:
if f.fieldname not in valid_fields:
for row in filter_fields:
if row.fieldname not in valid_fields:
frappe.throw(
_(
"Filter Fields Row #{0}: Fieldname <b>{1}</b> must be of type 'Link' or 'Table MultiSelect'"
).format(f.idx, f.fieldname)
"Filter Fields Row #{0}: Fieldname {1} must be of type 'Link' or 'Table MultiSelect'"
).format(row.idx, frappe.bold(row.fieldname))
)

def validate_attribute_filters(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest

Expand All @@ -11,44 +10,35 @@


class TestECommerceSettings(unittest.TestCase):
def setUp(self):
frappe.db.sql("""delete from `tabSingles` where doctype="Shipping Cart Settings" """)

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 tearDown(self):
frappe.db.rollback()

def test_tax_rule_validation(self):
frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0")
frappe.db.commit() # nosemgrep

cart_settings = self.get_cart_settings()
cart_settings = frappe.get_doc("E Commerce Settings")
cart_settings.enabled = 1
if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1}, "name"):
self.assertRaises(ShoppingCartSetupError, cart_settings.validate_tax_rule)

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):
"Accepts a dict of values that updates E Commerce Settings."
Expand Down
16 changes: 11 additions & 5 deletions erpnext/e_commerce/product_data_engine/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ def get_field_filters(self):
fields, filter_data = [], []
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
item_meta = frappe.get_meta("Item", cached=True)
fields = [item_meta.get_field(field) for field in filter_fields if item_meta.has_field(field)]
# filter valid field filters i.e. those that exist in Website Item
web_item_meta = frappe.get_meta("Website Item", cached=True)
fields = [
web_item_meta.get_field(field) for field in filter_fields if web_item_meta.has_field(field)
]

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)

if df.fieldtype == "Link":
Expand All @@ -50,9 +52,13 @@ def get_field_filters(self):
]
)

# 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
item_values = frappe.get_all(
"Item",
"Website Item",
fields=[df.fieldname],
filters=item_filters,
or_filters=item_or_filters,
Expand Down
48 changes: 48 additions & 0 deletions erpnext/e_commerce/product_data_engine/test_product_data_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,54 @@ def test_product_list_with_variants(self):
# tear down
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():
"Create Variant and Template Website Items."
Expand Down
16 changes: 15 additions & 1 deletion erpnext/hr/doctype/employee/employee.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
"holiday_list",
"default_shift",
"salary_information",
"salary_currency",
"ctc",
"salary_mode",
"payroll_cost_center",
"column_break_52",
Expand Down Expand Up @@ -807,13 +809,25 @@
"fieldtype": "Link",
"label": "Shift Request Approver",
"options": "User"
},
{
"fieldname": "salary_currency",
"fieldtype": "Link",
"label": "Salary Currency",
"options": "Currency"
},
{
"fieldname": "ctc",
"fieldtype": "Currency",
"label": "Cost to Company (CTC)",
"options": "salary_currency"
}
],
"icon": "fa fa-user",
"idx": 24,
"image_field": "image",
"links": [],
"modified": "2022-03-22 13:44:37.088519",
"modified": "2022-04-22 16:21:55.811983",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee",
Expand Down
Loading

0 comments on commit 783793d

Please sign in to comment.