Compare commits

..

44 Commits

Author SHA1 Message Date
Sagar Sharma
320f3c1ac0 Merge branch 'develop' into update-codeowners 2023-02-16 14:25:19 +05:30
HENRY Florian
4d79ffe3c6 fix: rename duplicate field name with same type into a DocType to avoid import Error (#34053)
* fix: Delivery Note field label set

* fix: Item field label de-duplicate name

* fix: Payment Entry field label de-duplicate name

* fix: Pruicing Rule field label de-duplicate name

* fix: Project field label de-duplicate name

* fix: Timesheet field label de-duplicate name

* Update erpnext/accounts/doctype/pricing_rule/pricing_rule.json

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>

* Update erpnext/stock/doctype/item/item.json

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>

---------

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2023-02-16 13:46:19 +05:30
Sagar Sharma
e8b8c51d82 chore: update CODEOWNERS 2023-02-16 12:55:37 +05:30
Tej Pochiraju
3f44ef8790 Allow bulk import of party specific item (#34083)
Co-authored-by: Tej Pochiraju <tej@iotready.co>
2023-02-16 10:11:25 +05:30
Sagar Sharma
c793fb0bb4 Merge pull request #34086 from s-aga-r/github-issue-33930
fix: create `Delivery Trip` from `Delivery Note` list
2023-02-15 22:40:10 +05:30
Sagar Sharma
c7a540b7e2 Merge branch 'develop' into github-issue-33930 2023-02-15 22:37:52 +05:30
s-aga-r
6c6195bae6 fix: create Delivery Trip from Delivery Note list 2023-02-15 22:37:26 +05:30
Sagar Sharma
0cd69c8700 Merge pull request #34080 from s-aga-r/batch-no-item
chore: copy `item_code` to `Batch` while creating new batch from SCR
2023-02-15 21:39:07 +05:30
s-aga-r
ffa9c6e4d9 chore: copy item_code to Batch while creating new batch from SCR 2023-02-15 20:16:42 +05:30
Anand Baburajan
d3ff289779 Merge pull request #34057 from AnandBaburajan/more_asset_bug_fixes
fix: manual depr entries in asset_depreciations_and_balances report and some misc bugs [develop]
2023-02-15 12:32:35 +05:30
Anand Baburajan
ef56060166 Merge branch 'develop' into more_asset_bug_fixes 2023-02-15 11:55:55 +05:30
rohitwaghchaure
2e43123048 Merge pull request #34065 from barredterra/print-rfq-letterhead
fix: change parameter name for letter head
2023-02-15 09:31:06 +05:30
rohitwaghchaure
9235918d65 Merge pull request #34061 from rohitwaghchaure/feat-allow-to-make-in-transit-entry
feat: allow to make in transit transfer entry from material request
2023-02-15 09:08:22 +05:30
barredterra
d5b7f2e49e test: download RFQ PDF 2023-02-14 19:53:39 +01:00
barredterra
8e40c04494 refactor: download RFQ PDF 2023-02-14 19:53:03 +01:00
barredterra
f7fd30fecf fix: change parameter name for letter head
To match changes from https://github.com/frappe/frappe/pull/19627
2023-02-14 19:34:36 +01:00
Deepesh Garg
e69ea17f59 Merge pull request #34059 from AnandBaburajan/anand_codeowners
chore: add anand to asset's codeowner
2023-02-14 19:38:46 +05:30
Rohit Waghchaure
5b6128848f feat: allow to make in transit transfer entry from material request 2023-02-14 19:03:07 +05:30
Anand Baburajan
662ebc5c1a Merge branch 'develop' into anand_codeowners 2023-02-14 18:35:05 +05:30
anandbaburajan
d003370f61 chore: add anand to asset's codeowner 2023-02-14 18:33:01 +05:30
Anand Baburajan
b0cfb0b9ef Merge branch 'develop' into more_asset_bug_fixes 2023-02-14 18:09:55 +05:30
anandbaburajan
a58f2e6c03 fix: asset_depreciation_and_balances report doesn't reflect manual depr entries 2023-02-14 17:59:16 +05:30
Sagar Sharma
75d9dbca43 Merge pull request #34018 from s-aga-r/fix-so-on-hold
fix: update `reserved_qty` when `Sales Order` marked as `Hold`
2023-02-14 16:51:56 +05:30
Sagar Sharma
447310702e Merge branch 'develop' into fix-so-on-hold 2023-02-14 16:01:43 +05:30
ruthra kumar
960085853a Merge pull request #34048 from ruthra-kumar/typeerror_on_sales_order_analysis
fix: typeerror on Sales Order analysis report
2023-02-14 15:27:36 +05:30
Sagar Sharma
5faa62c49a Merge branch 'develop' into fix-so-on-hold 2023-02-14 11:06:10 +05:30
rohitwaghchaure
3014bac293 Merge pull request #34046 from FHenry/dev_fix_BOM_import
fix: BOM import failed as use same label field for Raw Materials Item table and Scrap Item table
2023-02-14 10:29:26 +05:30
ruthra kumar
00a4191966 fix: typeerror on Sales Order analysis report 2023-02-14 09:56:35 +05:30
Raffael Meyer
770369e5c1 feat: translate fixtures during runtime, not installation (#33996)
feat: install untranslated fixtures from files

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-14 09:29:27 +05:30
Florian HENRY
86be259341 fix: BOM import failed as importer use same label field for Raw MaterialsItem table and Scrap Item table 2023-02-13 23:34:20 +01:00
Deepesh Garg
ce748cec3a fix: Amount validation in Payment Request against Purchase Order (#34042) 2023-02-13 20:56:24 +05:30
HENRY Florian
231fe4156f fix: should never get cutomer price on purchase document (#34002)
* fix: never get cutomer price on purchase document

chores: syntax

chore: typo in stock_entry get_uom_details (#33998)

fix: typo in stock_entry get_uom_details

chores: syntax

* feat: add test for get_item_detail price list oriented

* feat: add test for get_item_detail price price oriented

* feat: add test for get_item_detail price price oriented

* chore: clean test code
2023-02-13 20:56:05 +05:30
anandbaburajan
c533213225 fix: opening_accumulated_depreciation and precision in charts 2023-02-13 17:22:31 +05:30
Deepesh Garg
a67284e96d fix: Concurrency issues in Sales and Purchase returns (#34019) 2023-02-13 15:31:19 +05:30
ruthra kumar
4f2553e7b6 Merge pull request #34021 from ruthra-kumar/handle_rare_cases_of_null_in_outstanding_calculation
fix: rare instances of IntegrityError while cancelling journals against cr note
2023-02-13 14:14:27 +05:30
ruthra kumar
192a3395a5 refactor: filter only immediate upcoming payment term for each SO (#33923)
* fix: ignore closed or 'on hold' orders

* refactor: filter immediate upcoming term

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-13 12:36:40 +05:30
ruthra kumar
e5a2b15fba fix: unwanted difference amount calculation on cr note and invoice with same currency (#34020)
* fix: incorrect difference amount while reconiling cr/dr notes

* fix(test): catch incorrect difference amount calculation

Fixed issues where difference amount was calculated for Cr Notes and Invoices of
the same currency.

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-13 12:08:42 +05:30
Deepesh Garg
148703bfc2 feat: Setting to allow Sales Order creation against expired quotation (#33952)
* feat: Setting to allow Sales Order creation against expired quotation

* chore: linting issues
2023-02-13 10:27:55 +05:30
Deepesh Garg
ce3f9622cb Merge branch 'develop' into handle_rare_cases_of_null_in_outstanding_calculation 2023-02-13 09:42:41 +05:30
Deepesh Garg
48bb2c942b fix: Ignore Payment Ledger Entry on dunning cancel (#34025)
* fix: Ignore Payment Ledger Entry on dunning cancel

* chore: fix translation issue
2023-02-13 09:42:01 +05:30
Deepesh Garg
77f6789706 Revert "Update tr.csv" (#34026)
Revert "chore: Update Turkish translations (#33985)"

This reverts commit 6ea0a69d3a.
2023-02-13 08:27:00 +05:30
ruthra kumar
b9a7ff7c3d fix: IntegrityError while cancelling journals against cr note 2023-02-12 14:06:40 +05:30
Sagar Sharma
b0e8dfc1d0 Merge branch 'develop' into fix-so-on-hold 2023-02-12 13:37:21 +05:30
s-aga-r
d76759e066 fix: update reserved_qty when Sales Order marked as Hold 2023-02-12 13:36:26 +05:30
54 changed files with 710 additions and 469 deletions

View File

@@ -4,7 +4,7 @@
# the repo. Unless a later match takes precedence,
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/assets/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/assets/ @anandbaburajan @deepeshgarg007
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
@@ -16,6 +16,7 @@ erpnext/maintenance/ @rohitwaghchaure @s-aga-r
erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
erpnext/quality_management/ @rohitwaghchaure @s-aga-r
erpnext/stock/ @rohitwaghchaure @s-aga-r
erpnext/subcontracting @rohitwaghchaure @s-aga-r
erpnext/crm/ @NagariaHussain
erpnext/education/ @rutwikhdev

View File

@@ -40,7 +40,7 @@ class Dunning(AccountsController):
def on_cancel(self):
if self.dunning_amount:
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def make_gl_entries(self):

View File

@@ -239,7 +239,7 @@
"depends_on": "paid_from",
"fieldname": "paid_from_account_currency",
"fieldtype": "Link",
"label": "Account Currency",
"label": "Account Currency (From)",
"options": "Currency",
"print_hide": 1,
"read_only": 1,
@@ -249,7 +249,7 @@
"depends_on": "paid_from",
"fieldname": "paid_from_account_balance",
"fieldtype": "Currency",
"label": "Account Balance",
"label": "Account Balance (From)",
"options": "paid_from_account_currency",
"print_hide": 1,
"read_only": 1
@@ -272,7 +272,7 @@
"depends_on": "paid_to",
"fieldname": "paid_to_account_currency",
"fieldtype": "Link",
"label": "Account Currency",
"label": "Account Currency (To)",
"options": "Currency",
"print_hide": 1,
"read_only": 1,
@@ -282,7 +282,7 @@
"depends_on": "paid_to",
"fieldname": "paid_to_account_balance",
"fieldtype": "Currency",
"label": "Account Balance",
"label": "Account Balance (To)",
"options": "paid_to_account_currency",
"print_hide": 1,
"read_only": 1
@@ -304,7 +304,7 @@
{
"fieldname": "source_exchange_rate",
"fieldtype": "Float",
"label": "Exchange Rate",
"label": "Source Exchange Rate",
"precision": "9",
"print_hide": 1,
"reqd": 1
@@ -334,7 +334,7 @@
{
"fieldname": "target_exchange_rate",
"fieldtype": "Float",
"label": "Exchange Rate",
"label": "Target Exchange Rate",
"precision": "9",
"print_hide": 1,
"reqd": 1
@@ -633,14 +633,14 @@
"depends_on": "eval:doc.party_type == 'Supplier'",
"fieldname": "purchase_taxes_and_charges_template",
"fieldtype": "Link",
"label": "Taxes and Charges Template",
"label": "Purchase Taxes and Charges Template",
"options": "Purchase Taxes and Charges Template"
},
{
"depends_on": "eval: doc.party_type == 'Customer'",
"fieldname": "sales_taxes_and_charges_template",
"fieldtype": "Link",
"label": "Taxes and Charges Template",
"label": "Sales Taxes and Charges Template",
"options": "Sales Taxes and Charges Template"
},
{
@@ -733,7 +733,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2022-12-08 16:25:43.824051",
"modified": "2023-02-14 04:52:30.478523",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@@ -234,7 +234,7 @@ class PaymentReconciliation(Document):
def allocate_entries(self, args):
self.validate_entries()
invoice_exchange_map = self.get_invoice_exchange_map(args.get("invoices"))
invoice_exchange_map = self.get_invoice_exchange_map(args.get("invoices"), args.get("payments"))
default_exchange_gain_loss_account = frappe.get_cached_value(
"Company", self.company, "exchange_gain_loss_account"
)
@@ -253,6 +253,9 @@ class PaymentReconciliation(Document):
pay["amount"] = 0
inv["exchange_rate"] = invoice_exchange_map.get(inv.get("invoice_number"))
if pay.get("reference_type") in ["Sales Invoice", "Purchase Invoice"]:
pay["exchange_rate"] = invoice_exchange_map.get(pay.get("reference_name"))
res.difference_amount = self.get_difference_amount(pay, inv, res["allocated_amount"])
res.difference_account = default_exchange_gain_loss_account
res.exchange_rate = inv.get("exchange_rate")
@@ -407,13 +410,21 @@ class PaymentReconciliation(Document):
if not self.get("payments"):
frappe.throw(_("No records found in the Payments table"))
def get_invoice_exchange_map(self, invoices):
def get_invoice_exchange_map(self, invoices, payments):
sales_invoices = [
d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Sales Invoice"
]
sales_invoices.extend(
[d.get("reference_name") for d in payments if d.get("reference_type") == "Sales Invoice"]
)
purchase_invoices = [
d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Purchase Invoice"
]
purchase_invoices.extend(
[d.get("reference_name") for d in payments if d.get("reference_type") == "Purchase Invoice"]
)
invoice_exchange_map = frappe._dict()
if sales_invoices:

View File

@@ -473,6 +473,11 @@ class TestPaymentReconciliation(FrappeTestCase):
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
# Cr Note and Invoice are of the same currency. There shouldn't any difference amount.
for row in pr.allocation:
self.assertEqual(flt(row.get("difference_amount")), 0.0)
pr.reconcile()
pr.get_unreconciled_entries()
@@ -506,6 +511,11 @@ class TestPaymentReconciliation(FrappeTestCase):
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.allocation[0].allocated_amount = allocated_amount
# Cr Note and Invoice are of the same currency. There shouldn't any difference amount.
for row in pr.allocation:
self.assertEqual(flt(row.get("difference_amount")), 0.0)
pr.reconcile()
# assert outstanding

View File

@@ -45,21 +45,20 @@ class PaymentRequest(Document):
frappe.throw(_("To create a Payment Request reference document is required"))
def validate_payment_request_amount(self):
existing_payment_request_amount = get_existing_payment_request_amount(
self.reference_doctype, self.reference_name
existing_payment_request_amount = flt(
get_existing_payment_request_amount(self.reference_doctype, self.reference_name)
)
if existing_payment_request_amount:
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if not hasattr(ref_doc, "order_type") or getattr(ref_doc, "order_type") != "Shopping Cart":
ref_amount = get_amount(ref_doc, self.payment_account)
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if not hasattr(ref_doc, "order_type") or getattr(ref_doc, "order_type") != "Shopping Cart":
ref_amount = get_amount(ref_doc, self.payment_account)
if existing_payment_request_amount + flt(self.grand_total) > ref_amount:
frappe.throw(
_("Total Payment Request amount cannot be greater than {0} amount").format(
self.reference_doctype
)
if existing_payment_request_amount + flt(self.grand_total) > ref_amount:
frappe.throw(
_("Total Payment Request amount cannot be greater than {0} amount").format(
self.reference_doctype
)
)
def validate_currency(self):
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)

View File

@@ -472,7 +472,7 @@
"description": "If rate is zero them item will be treated as \"Free Item\"",
"fieldname": "free_item_rate",
"fieldtype": "Currency",
"label": "Rate"
"label": "Free Item Rate"
},
{
"collapsible": 1,
@@ -608,7 +608,7 @@
"icon": "fa fa-gift",
"idx": 1,
"links": [],
"modified": "2022-10-13 19:05:35.056304",
"modified": "2023-02-14 04:53:34.887358",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",

View File

@@ -135,6 +135,34 @@ def get_assets(filters):
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and ads.asset = a.name and ads.docstatus=1 and ads.name = ds.parent and ifnull(ds.journal_entry, '') != ''
group by a.asset_category
union
SELECT a.asset_category,
ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
gle.debit
else
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then
gle.debit
else
0
end), 0) as depreciation_eliminated_during_the_period,
ifnull(sum(case when gle.posting_date >= %(from_date)s and gle.posting_date <= %(to_date)s
and (ifnull(a.disposal_date, 0) = 0 or gle.posting_date <= a.disposal_date) then
gle.debit
else
0
end), 0) as depreciation_amount_during_the_period
from `tabGL Entry` gle
join `tabAsset` a on
gle.against_voucher = a.name
join `tabAsset Category Account` aca on
aca.parent = a.asset_category and aca.company_name = %(company)s
join `tabCompany` company on
company.name = %(company)s
where a.docstatus=1 and a.company=%(company)s and a.calculate_depreciation=0 and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
group by a.asset_category
union
SELECT a.asset_category,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
0

View File

@@ -1512,9 +1512,12 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa
ref_doc = frappe.get_doc(voucher_type, voucher_no)
# Didn't use db_set for optimisation purpose
ref_doc.outstanding_amount = outstanding["outstanding_in_account_currency"]
ref_doc.outstanding_amount = outstanding["outstanding_in_account_currency"] or 0.0
frappe.db.set_value(
voucher_type, voucher_no, "outstanding_amount", outstanding["outstanding_in_account_currency"]
voucher_type,
voucher_no,
"outstanding_amount",
outstanding["outstanding_in_account_currency"] or 0.0,
)
ref_doc.set_status(update=True)

View File

@@ -209,62 +209,62 @@ frappe.ui.form.on('Asset', {
return
}
var x_intervals = [frm.doc.purchase_date];
var x_intervals = [frappe.format(frm.doc.purchase_date, { fieldtype: 'Date' })];
var asset_values = [frm.doc.gross_purchase_amount];
var last_depreciation_date = frm.doc.purchase_date;
if(frm.doc.opening_accumulated_depreciation) {
last_depreciation_date = frappe.datetime.add_months(frm.doc.next_depreciation_date,
-1*frm.doc.frequency_of_depreciation);
x_intervals.push(last_depreciation_date);
asset_values.push(flt(frm.doc.gross_purchase_amount) -
flt(frm.doc.opening_accumulated_depreciation));
}
if(frm.doc.calculate_depreciation) {
if (frm.doc.finance_books.length == 1) {
let depr_schedule = (await frappe.call(
"erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule.get_depr_schedule",
{
asset_name: frm.doc.name,
status: frm.doc.docstatus ? "Active" : "Draft",
finance_book: frm.doc.finance_books[0].finance_book || null
}
)).message;
$.each(depr_schedule || [], function(i, v) {
x_intervals.push(v.schedule_date);
var asset_value = flt(frm.doc.gross_purchase_amount) - flt(v.accumulated_depreciation_amount);
if(v.journal_entry) {
last_depreciation_date = v.schedule_date;
asset_values.push(asset_value);
} else {
if (in_list(["Scrapped", "Sold"], frm.doc.status)) {
asset_values.push(null);
} else {
asset_values.push(asset_value)
}
}
});
if(frm.doc.opening_accumulated_depreciation) {
var depreciation_date = frappe.datetime.add_months(
frm.doc.finance_books[0].depreciation_start_date,
-1 * frm.doc.finance_books[0].frequency_of_depreciation
);
x_intervals.push(frappe.format(depreciation_date, { fieldtype: 'Date' }));
asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount')));
}
let depr_schedule = (await frappe.call(
"erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule.get_depr_schedule",
{
asset_name: frm.doc.name,
status: frm.doc.docstatus ? "Active" : "Draft",
finance_book: frm.doc.finance_books[0].finance_book || null
}
)).message;
$.each(depr_schedule || [], function(i, v) {
x_intervals.push(frappe.format(v.schedule_date, { fieldtype: 'Date' }));
var asset_value = flt(frm.doc.gross_purchase_amount - v.accumulated_depreciation_amount, precision('gross_purchase_amount'));
if(v.journal_entry) {
asset_values.push(asset_value);
} else {
if (in_list(["Scrapped", "Sold"], frm.doc.status)) {
asset_values.push(null);
} else {
asset_values.push(asset_value)
}
}
});
} else {
if(frm.doc.opening_accumulated_depreciation) {
x_intervals.push(frappe.format(frm.doc.creation.split(" ")[0], { fieldtype: 'Date' }));
asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount')));
}
let depr_entries = (await frappe.call({
method: "get_manual_depreciation_entries",
doc: frm.doc,
})).message;
$.each(depr_entries || [], function(i, v) {
x_intervals.push(v.posting_date);
last_depreciation_date = v.posting_date;
x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' }));
let last_asset_value = asset_values[asset_values.length - 1]
asset_values.push(last_asset_value - v.value);
});
}
if(in_list(["Scrapped", "Sold"], frm.doc.status)) {
x_intervals.push(frm.doc.disposal_date);
x_intervals.push(frappe.format(frm.doc.disposal_date, { fieldtype: 'Date' }));
asset_values.push(0);
last_depreciation_date = frm.doc.disposal_date;
}
frm.dashboard.render_graph({

View File

@@ -429,25 +429,16 @@ class Asset(AccountsController):
def get_value_after_depreciation(self, finance_book=None):
if not self.calculate_depreciation:
return self.value_after_depreciation
return flt(self.value_after_depreciation, self.precision("gross_purchase_amount"))
if not finance_book:
return self.get("finance_books")[0].value_after_depreciation
return flt(
self.get("finance_books")[0].value_after_depreciation, self.precision("gross_purchase_amount")
)
for row in self.get("finance_books"):
if finance_book == row.finance_book:
return row.value_after_depreciation
def _get_value_after_depreciation_for_making_schedule(self, fb_row):
# value_after_depreciation - current Asset value
if self.docstatus == 1 and fb_row.value_after_depreciation:
value_after_depreciation = flt(fb_row.value_after_depreciation)
else:
value_after_depreciation = flt(self.gross_purchase_amount) - flt(
self.opening_accumulated_depreciation
)
return value_after_depreciation
return flt(row.value_after_depreciation, self.precision("gross_purchase_amount"))
def get_default_finance_book_idx(self):
if not self.get("default_finance_book") and self.company:

View File

@@ -134,7 +134,7 @@ class AssetDepreciationSchedule(Document):
):
asset_doc.validate_asset_finance_books(row)
value_after_depreciation = asset_doc._get_value_after_depreciation_for_making_schedule(row)
value_after_depreciation = _get_value_after_depreciation_for_making_schedule(asset_doc, row)
row.value_after_depreciation = value_after_depreciation
if update_asset_finance_book_row:
@@ -325,6 +325,17 @@ class AssetDepreciationSchedule(Document):
)
def _get_value_after_depreciation_for_making_schedule(asset_doc, fb_row):
if asset_doc.docstatus == 1 and fb_row.value_after_depreciation:
value_after_depreciation = flt(fb_row.value_after_depreciation)
else:
value_after_depreciation = flt(asset_doc.gross_purchase_amount) - flt(
asset_doc.opening_accumulated_depreciation
)
return value_after_depreciation
def make_draft_asset_depr_schedules_if_not_present(asset_doc):
for row in asset_doc.get("finance_books"):
draft_asset_depr_schedule_name = get_asset_depr_schedule_name(

View File

@@ -5,7 +5,7 @@
import frappe
from frappe import _
from frappe.query_builder.functions import Sum
from frappe.utils import cstr, formatdate, getdate
from frappe.utils import cstr, flt, formatdate, getdate
from erpnext.accounts.report.financial_statements import (
get_fiscal_year_data,
@@ -102,13 +102,9 @@ def get_data(filters):
]
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
finance_book_filter = ("is", "not set")
if filters.finance_book:
finance_book_filter = ("=", filters.finance_book)
assets_linked_to_fb = frappe.db.get_all(
doctype="Asset Finance Book",
filters={"finance_book": finance_book_filter},
filters={"finance_book": filters.finance_book or ("is", "not set")},
pluck="parent",
)
@@ -194,7 +190,7 @@ def get_depreciation_amount_of_asset(asset, depreciation_amount_map, filters):
else:
depr_amount = get_manual_depreciation_amount_of_asset(asset, filters)
return depr_amount
return flt(depr_amount, 2)
def get_finance_book_value_map(filters):

View File

@@ -124,12 +124,11 @@ frappe.ui.form.on("Request for Quotation",{
frappe.urllib.get_full_url(
"/api/method/erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_pdf?" +
new URLSearchParams({
doctype: frm.doc.doctype,
name: frm.doc.name,
supplier: data.supplier,
print_format: data.print_format || "Standard",
language: data.language || frappe.boot.lang,
letter_head: data.letter_head || frm.doc.letter_head || "",
letterhead: data.letter_head || frm.doc.letter_head || "",
}).toString()
)
);

View File

@@ -3,6 +3,7 @@
import json
from typing import Optional
import frappe
from frappe import _
@@ -388,24 +389,26 @@ def create_rfq_items(sq_doc, supplier, data):
@frappe.whitelist()
def get_pdf(doctype, name, supplier, print_format=None, language=None, letter_head=None):
# permissions get checked in `download_pdf`
if doc := get_rfq_doc(doctype, name, supplier):
download_pdf(
doctype,
name,
print_format,
doc=doc,
language=language,
letter_head=letter_head or None,
)
def get_rfq_doc(doctype, name, supplier):
def get_pdf(
name: str,
supplier: str,
print_format: Optional[str] = None,
language: Optional[str] = None,
letterhead: Optional[str] = None,
):
doc = frappe.get_doc("Request for Quotation", name)
if supplier:
doc = frappe.get_doc(doctype, name)
doc.update_supplier_part_no(supplier)
return doc
# permissions get checked in `download_pdf`
download_pdf(
doc.doctype,
doc.name,
print_format,
doc=doc,
language=language,
letterhead=letterhead or None,
)
@frappe.whitelist()

View File

@@ -8,6 +8,7 @@ from frappe.utils import nowdate
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import (
create_supplier_quotation,
get_pdf,
make_supplier_quotation_from_rfq,
)
from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq
@@ -124,6 +125,11 @@ class TestRequestforQuotation(FrappeTestCase):
rfq.status = "Draft"
rfq.submit()
def test_get_pdf(self):
rfq = make_request_for_quotation()
get_pdf(rfq.name, rfq.get("suppliers")[0].supplier)
self.assertEqual(frappe.local.response.type, "pdf")
def make_request_for_quotation(**args):
"""

View File

@@ -252,6 +252,7 @@ def get_already_returned_items(doc):
child.parent = par.name and par.docstatus = 1
and par.is_return = 1 and par.return_against = %s
group by item_code
for update
""".format(
column, doc.doctype, doc.doctype
),

View File

@@ -26,10 +26,11 @@
}
],
"links": [],
"modified": "2021-02-08 12:51:48.971517",
"modified": "2023-02-10 00:51:44.973957",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead Source",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
@@ -58,5 +59,7 @@
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
"sort_order": "DESC",
"states": [],
"translated_doctype": 1
}

View File

@@ -18,10 +18,11 @@
}
],
"links": [],
"modified": "2020-05-20 12:22:01.866472",
"modified": "2023-02-10 01:40:23.713390",
"modified_by": "Administrator",
"module": "CRM",
"name": "Sales Stage",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
@@ -40,5 +41,7 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
"states": [],
"track_changes": 1,
"translated_doctype": 1
}

View File

@@ -289,7 +289,7 @@
{
"fieldname": "scrap_items",
"fieldtype": "Table",
"label": "Items",
"label": "Scrap Items",
"options": "BOM Scrap Item"
},
{
@@ -605,7 +605,7 @@
"image_field": "image",
"is_submittable": 1,
"links": [],
"modified": "2023-01-10 07:47:08.652616",
"modified": "2023-02-13 17:31:37.504565",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM",

View File

@@ -1,16 +1,17 @@
import frappe
from frappe import _
def execute():
from erpnext.setup.setup_wizard.operations.install_fixtures import default_sales_partner_type
from erpnext.setup.setup_wizard.operations.install_fixtures import read_lines
frappe.reload_doc("selling", "doctype", "sales_partner_type")
frappe.local.lang = frappe.db.get_default("lang") or "en"
default_sales_partner_type = read_lines("sales_partner_type.txt")
for s in default_sales_partner_type:
insert_sales_partner_type(_(s))
insert_sales_partner_type(s)
# get partner type in existing forms (customized)
# and create a document if not created

View File

@@ -408,7 +408,7 @@
"depends_on": "eval:(doc.frequency == \"Daily\" && doc.collect_progress == true)",
"fieldname": "daily_time_to_send",
"fieldtype": "Time",
"label": "Time to send"
"label": "Daily Time to send"
},
{
"depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)",
@@ -421,7 +421,7 @@
"depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)",
"fieldname": "weekly_time_to_send",
"fieldtype": "Time",
"label": "Time to send"
"label": "Weekly Time to send"
},
{
"fieldname": "column_break_45",
@@ -451,7 +451,7 @@
"index_web_pages_for_search": 1,
"links": [],
"max_attachments": 4,
"modified": "2022-06-23 16:45:06.108499",
"modified": "2023-02-14 04:54:25.819620",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project",
@@ -497,4 +497,4 @@
"timeline_field": "customer",
"title_field": "project_name",
"track_seen": 1
}
}

View File

@@ -282,21 +282,21 @@
{
"fieldname": "base_total_costing_amount",
"fieldtype": "Currency",
"label": "Total Costing Amount",
"label": "Base Total Costing Amount",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "base_total_billable_amount",
"fieldtype": "Currency",
"label": "Total Billable Amount",
"label": "Base Total Billable Amount",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "base_total_billed_amount",
"fieldtype": "Currency",
"label": "Total Billed Amount",
"label": "Base Total Billed Amount",
"print_hide": 1,
"read_only": 1
},
@@ -311,10 +311,11 @@
"idx": 1,
"is_submittable": 1,
"links": [],
"modified": "2022-06-15 22:08:53.930200",
"modified": "2023-02-14 04:55:41.735991",
"modified_by": "Administrator",
"module": "Projects",
"name": "Timesheet",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
@@ -388,5 +389,6 @@
],
"sort_field": "modified",
"sort_order": "ASC",
"states": [],
"title_field": "title"
}

View File

@@ -1,123 +1,68 @@
{
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:industry",
"beta": 0,
"creation": "2012-03-27 14:36:09",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"editable_grid": 0,
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:industry",
"creation": "2012-03-27 14:36:09",
"doctype": "DocType",
"document_type": "Setup",
"engine": "InnoDB",
"field_order": [
"industry"
],
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "industry",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Industry",
"length": 0,
"no_copy": 0,
"oldfieldname": "industry",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
"fieldname": "industry",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Industry",
"oldfieldname": "industry",
"oldfieldtype": "Data",
"reqd": 1,
"unique": 1
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-flag",
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2020-09-18 17:26:09.703215",
"modified_by": "Administrator",
"module": "Selling",
"name": "Industry Type",
"owner": "Administrator",
],
"icon": "fa fa-flag",
"idx": 1,
"links": [],
"modified": "2023-02-10 03:14:40.735763",
"modified_by": "Administrator",
"module": "Selling",
"name": "Industry Type",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"email": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
},
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
},
"email": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User"
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Master Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"email": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Master Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"track_seen": 0
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"translated_doctype": 1
}

View File

@@ -1,5 +1,6 @@
{
"actions": [],
"allow_import": 1,
"creation": "2021-08-27 19:28:07.559978",
"doctype": "DocType",
"editable_grid": 1,
@@ -51,7 +52,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2021-09-14 13:27:58.612334",
"modified": "2023-02-15 13:00:50.379713",
"modified_by": "Administrator",
"module": "Selling",
"name": "Party Specific Item",
@@ -72,6 +73,7 @@
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "party",
"track_changes": 1
}

View File

@@ -85,11 +85,15 @@ erpnext.selling.QuotationController = class QuotationController extends erpnext.
}
if (doc.docstatus == 1 && !["Lost", "Ordered"].includes(doc.status)) {
this.frm.add_custom_button(
__("Sales Order"),
this.frm.cscript["Make Sales Order"],
__("Create")
);
if (frappe.boot.sysdefaults.allow_sales_order_creation_for_expired_quotation
|| (!doc.valid_till)
|| frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) >= 0) {
this.frm.add_custom_button(
__("Sales Order"),
this.frm.cscript["Make Sales Order"],
__("Create")
);
}
if(doc.status!=="Ordered") {
this.frm.add_custom_button(__('Set as Lost'), () => {

View File

@@ -195,6 +195,17 @@ def get_list_context(context=None):
@frappe.whitelist()
def make_sales_order(source_name: str, target_doc=None):
if not frappe.db.get_singles_value(
"Selling Settings", "allow_sales_order_creation_for_expired_quotation"
):
quotation = frappe.db.get_value(
"Quotation", source_name, ["transaction_date", "valid_till"], as_dict=1
)
if quotation.valid_till and (
quotation.valid_till < quotation.transaction_date or quotation.valid_till < getdate(nowdate())
):
frappe.throw(_("Validity period of this quotation has ended."))
return _make_sales_order(source_name, target_doc)

View File

@@ -144,11 +144,21 @@ class TestQuotation(FrappeTestCase):
def test_so_from_expired_quotation(self):
from erpnext.selling.doctype.quotation.quotation import make_sales_order
frappe.db.set_single_value(
"Selling Settings", "allow_sales_order_creation_for_expired_quotation", 0
)
quotation = frappe.copy_doc(test_records[0])
quotation.valid_till = add_days(nowdate(), -1)
quotation.insert()
quotation.submit()
self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name)
frappe.db.set_single_value(
"Selling Settings", "allow_sales_order_creation_for_expired_quotation", 1
)
make_sales_order(quotation.name)
def test_shopping_cart_without_website_item(self):

View File

@@ -1,94 +1,47 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:sales_partner_type",
"beta": 0,
"creation": "2018-06-11 13:15:57.404716",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"autoname": "field:sales_partner_type",
"creation": "2018-06-11 13:15:57.404716",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"sales_partner_type"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sales_partner_type",
"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": "Sales Partner Type",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldname": "sales_partner_type",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Sales Partner Type",
"reqd": 1,
"unique": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-06-11 13:45:13.554307",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Partner Type",
"name_case": "",
"owner": "Administrator",
],
"links": [],
"modified": "2023-02-10 01:00:20.110800",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Partner Type",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"translated_doctype": 1
}

View File

@@ -27,6 +27,7 @@
"column_break_5",
"allow_multiple_items",
"allow_against_multiple_purchase_orders",
"allow_sales_order_creation_for_expired_quotation",
"hide_tax_id",
"enable_discount_accounting"
],
@@ -172,6 +173,12 @@
"fieldname": "enable_discount_accounting",
"fieldtype": "Check",
"label": "Enable Discount Accounting for Selling"
},
{
"default": "0",
"fieldname": "allow_sales_order_creation_for_expired_quotation",
"fieldtype": "Check",
"label": "Allow Sales Order Creation For Expired Quotation"
}
],
"icon": "fa fa-cog",
@@ -179,7 +186,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2022-05-31 19:39:48.398738",
"modified": "2023-02-04 12:37:53.380857",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling Settings",

View File

@@ -103,6 +103,11 @@ function get_filters() {
return options
}
},
{
"fieldname":"only_immediate_upcoming_term",
"label": __("Show only the Immediate Upcoming Term"),
"fieldtype": "Check",
},
]
return filters;
}

View File

@@ -4,6 +4,7 @@
import frappe
from frappe import _, qb, query_builder
from frappe.query_builder import Criterion, functions
from frappe.utils.dateutils import getdate
def get_columns():
@@ -208,6 +209,7 @@ def get_so_with_invoices(filters):
)
.where(
(so.docstatus == 1)
& (so.status.isin(["To Deliver and Bill", "To Bill"]))
& (so.payment_terms_template != "NULL")
& (so.company == conditions.company)
& (so.transaction_date[conditions.start_date : conditions.end_date])
@@ -291,6 +293,18 @@ def filter_on_calculated_status(filters, sales_orders):
return sales_orders
def filter_for_immediate_upcoming_term(filters, sales_orders):
if filters.only_immediate_upcoming_term and sales_orders:
immediate_term_found = set()
filtered_data = []
for order in sales_orders:
if order.name not in immediate_term_found and order.due_date > getdate():
filtered_data.append(order)
immediate_term_found.add(order.name)
return filtered_data
return sales_orders
def execute(filters=None):
columns = get_columns()
sales_orders, so_invoices = get_so_with_invoices(filters)
@@ -298,6 +312,8 @@ def execute(filters=None):
sales_orders = filter_on_calculated_status(filters, sales_orders)
sales_orders = filter_for_immediate_upcoming_term(filters, sales_orders)
prepare_chart(sales_orders)
data = sales_orders

View File

@@ -175,7 +175,9 @@ def prepare_data(data, so_elapsed_time, filters):
# update existing entry
so_row = sales_order_map[so_name]
so_row["required_date"] = max(getdate(so_row["delivery_date"]), getdate(row["delivery_date"]))
so_row["delay"] = min(so_row["delay"], row["delay"])
so_row["delay"] = (
min(so_row["delay"], row["delay"]) if row["delay"] and so_row["delay"] else so_row["delay"]
)
# sum numeric columns
fields = [

View File

@@ -31,7 +31,7 @@
"icon": "fa fa-bookmark",
"idx": 1,
"links": [],
"modified": "2022-06-28 17:10:26.853753",
"modified": "2023-02-10 01:53:41.319386",
"modified_by": "Administrator",
"module": "Setup",
"name": "Designation",
@@ -58,5 +58,6 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "ASC",
"states": []
"states": [],
"translated_doctype": 1
}

View File

@@ -0,0 +1,31 @@
Accountant
Administrative Assistant
Administrative Officer
Analyst
Associate
Business Analyst
Business Development Manager
Consultant
Chief Executive Officer
Chief Financial Officer
Chief Operating Officer
Chief Technology Officer
Customer Service Representative
Designer
Engineer
Executive Assistant
Finance Manager
HR Manager
Head of Marketing and Sales
Manager
Managing Director
Marketing Manager
Marketing Specialist
President
Product Manager
Project Manager
Researcher
Sales Representative
Secretary
Software Developer
Vice President

View File

@@ -1,57 +0,0 @@
from frappe import _
def get_industry_types():
return [
_("Accounting"),
_("Advertising"),
_("Aerospace"),
_("Agriculture"),
_("Airline"),
_("Apparel & Accessories"),
_("Automotive"),
_("Banking"),
_("Biotechnology"),
_("Broadcasting"),
_("Brokerage"),
_("Chemical"),
_("Computer"),
_("Consulting"),
_("Consumer Products"),
_("Cosmetics"),
_("Defense"),
_("Department Stores"),
_("Education"),
_("Electronics"),
_("Energy"),
_("Entertainment & Leisure"),
_("Executive Search"),
_("Financial Services"),
_("Food, Beverage & Tobacco"),
_("Grocery"),
_("Health Care"),
_("Internet Publishing"),
_("Investment Banking"),
_("Legal"),
_("Manufacturing"),
_("Motion Picture & Video"),
_("Music"),
_("Newspaper Publishers"),
_("Online Auctions"),
_("Pension Funds"),
_("Pharmaceuticals"),
_("Private Equity"),
_("Publishing"),
_("Real Estate"),
_("Retail & Wholesale"),
_("Securities & Commodity Exchanges"),
_("Service"),
_("Soap & Detergent"),
_("Software"),
_("Sports"),
_("Technology"),
_("Telecommunications"),
_("Television"),
_("Transportation"),
_("Venture Capital"),
]

View File

@@ -0,0 +1,51 @@
Accounting
Advertising
Aerospace
Agriculture
Airline
Apparel & Accessories
Automotive
Banking
Biotechnology
Broadcasting
Brokerage
Chemical
Computer
Consulting
Consumer Products
Cosmetics
Defense
Department Stores
Education
Electronics
Energy
Entertainment & Leisure
Executive Search
Financial Services
Food, Beverage & Tobacco
Grocery
Health Care
Internet Publishing
Investment Banking
Legal
Manufacturing
Motion Picture & Video
Music
Newspaper Publishers
Online Auctions
Pension Funds
Pharmaceuticals
Private Equity
Publishing
Real Estate
Retail & Wholesale
Securities & Commodity Exchanges
Service
Soap & Detergent
Software
Sports
Technology
Telecommunications
Television
Transportation
Venture Capital

View File

@@ -0,0 +1,10 @@
Existing Customer
Reference
Advertisement
Cold Calling
Exhibition
Supplier Reference
Mass Mailing
Customer's Vendor
Campaign
Walk In

View File

@@ -0,0 +1,7 @@
Channel Partner
Distributor
Dealer
Agent
Retailer
Implementation Partner
Reseller

View File

@@ -0,0 +1,8 @@
Prospecting
Qualification
Needs Analysis
Value Proposition
Identifying Decision Makers
Perception Analysis
Proposal/Price Quote
Negotiation/Review

View File

@@ -4,6 +4,7 @@
import json
import os
from pathlib import Path
import frappe
from frappe import _
@@ -16,28 +17,10 @@ from frappe.utils import cstr, getdate
from erpnext.accounts.doctype.account.account import RootNotEditable
from erpnext.regional.address_template.setup import set_up_address_templates
default_lead_sources = [
"Existing Customer",
"Reference",
"Advertisement",
"Cold Calling",
"Exhibition",
"Supplier Reference",
"Mass Mailing",
"Customer's Vendor",
"Campaign",
"Walk In",
]
default_sales_partner_type = [
"Channel Partner",
"Distributor",
"Dealer",
"Agent",
"Retailer",
"Implementation Partner",
"Reseller",
]
def read_lines(filename: str) -> list[str]:
"""Return a list of lines from a file in the data directory."""
return (Path(__file__).parent.parent / "data" / filename).read_text().splitlines()
def install(country=None):
@@ -85,7 +68,11 @@ def install(country=None):
# Stock Entry Type
{"doctype": "Stock Entry Type", "name": "Material Issue", "purpose": "Material Issue"},
{"doctype": "Stock Entry Type", "name": "Material Receipt", "purpose": "Material Receipt"},
{"doctype": "Stock Entry Type", "name": "Material Transfer", "purpose": "Material Transfer"},
{
"doctype": "Stock Entry Type",
"name": "Material Transfer",
"purpose": "Material Transfer",
},
{"doctype": "Stock Entry Type", "name": "Manufacture", "purpose": "Manufacture"},
{"doctype": "Stock Entry Type", "name": "Repack", "purpose": "Repack"},
{
@@ -103,22 +90,6 @@ def install(country=None):
"name": "Material Consumption for Manufacture",
"purpose": "Material Consumption for Manufacture",
},
# Designation
{"doctype": "Designation", "designation_name": _("CEO")},
{"doctype": "Designation", "designation_name": _("Manager")},
{"doctype": "Designation", "designation_name": _("Analyst")},
{"doctype": "Designation", "designation_name": _("Engineer")},
{"doctype": "Designation", "designation_name": _("Accountant")},
{"doctype": "Designation", "designation_name": _("Secretary")},
{"doctype": "Designation", "designation_name": _("Associate")},
{"doctype": "Designation", "designation_name": _("Administrative Officer")},
{"doctype": "Designation", "designation_name": _("Business Development Manager")},
{"doctype": "Designation", "designation_name": _("HR Manager")},
{"doctype": "Designation", "designation_name": _("Project Manager")},
{"doctype": "Designation", "designation_name": _("Head of Marketing and Sales")},
{"doctype": "Designation", "designation_name": _("Software Developer")},
{"doctype": "Designation", "designation_name": _("Designer")},
{"doctype": "Designation", "designation_name": _("Researcher")},
# territory: with two default territories, one for home country and one named Rest of the World
{
"doctype": "Territory",
@@ -291,28 +262,18 @@ def install(country=None):
{"doctype": "Market Segment", "market_segment": _("Lower Income")},
{"doctype": "Market Segment", "market_segment": _("Middle Income")},
{"doctype": "Market Segment", "market_segment": _("Upper Income")},
# Sales Stages
{"doctype": "Sales Stage", "stage_name": _("Prospecting")},
{"doctype": "Sales Stage", "stage_name": _("Qualification")},
{"doctype": "Sales Stage", "stage_name": _("Needs Analysis")},
{"doctype": "Sales Stage", "stage_name": _("Value Proposition")},
{"doctype": "Sales Stage", "stage_name": _("Identifying Decision Makers")},
{"doctype": "Sales Stage", "stage_name": _("Perception Analysis")},
{"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")},
{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")},
# Warehouse Type
{"doctype": "Warehouse Type", "name": "Transit"},
]
from erpnext.setup.setup_wizard.data.industry_type import get_industry_types
records += [{"doctype": "Industry Type", "industry": d} for d in get_industry_types()]
# records += [{"doctype":"Operation", "operation": d} for d in get_operations()]
records += [{"doctype": "Lead Source", "source_name": _(d)} for d in default_lead_sources]
records += [
{"doctype": "Sales Partner Type", "sales_partner_type": _(d)} for d in default_sales_partner_type
]
for doctype, title_field, filename in (
("Designation", "designation_name", "designation.txt"),
("Sales Stage", "stage_name", "sales_stage.txt"),
("Industry Type", "industry", "industry_type.txt"),
("Lead Source", "source_name", "lead_source.txt"),
("Sales Partner Type", "sales_partner_type", "sales_partner_type.txt"),
):
records += [{"doctype": doctype, title_field: title} for title in read_lines(filename)]
base_path = frappe.get_app_path("erpnext", "stock", "doctype")
response = frappe.read_file(
@@ -397,7 +358,8 @@ def add_uom_data():
frappe.get_doc({"doctype": "UOM Category", "category_name": _(d.get("category"))}).db_insert()
if not frappe.db.exists(
"UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}
"UOM Conversion Factor",
{"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))},
):
frappe.get_doc(
{
@@ -535,7 +497,8 @@ def create_bank_account(args):
company_name = args.get("company_name")
bank_account_group = frappe.db.get_value(
"Account", {"account_type": "Bank", "is_group": 1, "root_type": "Asset", "company": company_name}
"Account",
{"account_type": "Bank", "is_group": 1, "root_type": "Asset", "company": company_name},
)
if bank_account_group:
bank_account = frappe.get_doc(

View File

@@ -25,6 +25,12 @@ def boot_session(bootinfo):
frappe.db.get_single_value("CRM Settings", "default_valid_till")
)
bootinfo.sysdefaults.allow_sales_order_creation_for_expired_quotation = cint(
frappe.db.get_single_value(
"Selling Settings", "allow_sales_order_creation_for_expired_quotation"
)
)
# if no company, show a dialog box to create a new company
bootinfo.customer_count = frappe.db.sql("""SELECT count(*) FROM `tabCustomer`""")[0][0]

View File

@@ -521,6 +521,7 @@
"allow_bulk_edit": 1,
"fieldname": "items",
"fieldtype": "Table",
"label": "Delivery Note Item",
"oldfieldname": "delivery_note_details",
"oldfieldtype": "Table",
"options": "Delivery Note Item",
@@ -666,6 +667,7 @@
{
"fieldname": "taxes",
"fieldtype": "Table",
"label": "Sales Taxes and Charges",
"oldfieldname": "other_charges",
"oldfieldtype": "Table",
"options": "Sales Taxes and Charges"
@@ -1401,7 +1403,7 @@
"idx": 146,
"is_submittable": 1,
"links": [],
"modified": "2022-12-12 18:38:53.067799",
"modified": "2023-02-14 04:45:44.179670",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",

View File

@@ -14,7 +14,7 @@ frappe.listview_settings['Delivery Note'] = {
return [__("Completed"), "green", "per_billed,=,100"];
}
},
onload: function (listview) {
onload: function (doclist) {
const action = () => {
const selected_docs = doclist.get_checked_items();
const docnames = doclist.get_checked_items(true);
@@ -56,14 +56,14 @@ frappe.listview_settings['Delivery Note'] = {
// doclist.page.add_actions_menu_item(__('Create Delivery Trip'), action, false);
listview.page.add_action_item(__('Create Delivery Trip'), action);
doclist.page.add_action_item(__('Create Delivery Trip'), action);
listview.page.add_action_item(__("Sales Invoice"), ()=>{
erpnext.bulk_transaction_processing.create(listview, "Delivery Note", "Sales Invoice");
doclist.page.add_action_item(__("Sales Invoice"), ()=>{
erpnext.bulk_transaction_processing.create(doclist, "Delivery Note", "Sales Invoice");
});
listview.page.add_action_item(__("Packaging Slip From Delivery Note"), ()=>{
erpnext.bulk_transaction_processing.create(listview, "Delivery Note", "Packing Slip");
doclist.page.add_action_item(__("Packaging Slip From Delivery Note"), ()=>{
erpnext.bulk_transaction_processing.create(doclist, "Delivery Note", "Packing Slip");
});
}
};

View File

@@ -706,7 +706,7 @@
"depends_on": "enable_deferred_expense",
"fieldname": "no_of_months_exp",
"fieldtype": "Int",
"label": "No of Months"
"label": "No of Months (Expense)"
},
{
"collapsible": 1,
@@ -911,7 +911,7 @@
"index_web_pages_for_search": 1,
"links": [],
"make_attachments_public": 1,
"modified": "2023-01-07 22:45:00.341745",
"modified": "2023-02-14 04:48:26.343620",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@@ -38,5 +38,19 @@
"price_list_rate": 1000,
"valid_from": "2017-04-10",
"valid_upto": "2017-04-17"
},
{
"doctype": "Item Price",
"item_code": "_Test Item",
"price_list": "_Test Buying Price List",
"price_list_rate": 100,
"supplier": "_Test Supplier"
},
{
"doctype": "Item Price",
"item_code": "_Test Item",
"price_list": "_Test Selling Price List",
"price_list_rate": 200,
"customer": "_Test Customer"
}
]

View File

@@ -110,8 +110,11 @@ frappe.ui.form.on('Material Request', {
if (frm.doc.material_request_type === "Material Transfer") {
add_create_pick_list_button();
frm.add_custom_button(__("Transfer Material"),
frm.add_custom_button(__("Material Transfer"),
() => frm.events.make_stock_entry(frm), __('Create'));
frm.add_custom_button(__("Material Transfer (In Transit)"),
() => frm.events.make_in_transit_stock_entry(frm), __('Create'));
}
if (frm.doc.material_request_type === "Material Issue") {
@@ -333,6 +336,46 @@ frappe.ui.form.on('Material Request', {
});
},
make_in_transit_stock_entry(frm) {
frappe.prompt(
[
{
label: __('In Transit Warehouse'),
fieldname: 'in_transit_warehouse',
fieldtype: 'Link',
options: 'Warehouse',
reqd: 1,
get_query: () => {
return{
filters: {
'company': frm.doc.company,
'is_group': 0,
'warehouse_type': 'Transit'
}
}
}
}
],
(values) => {
frappe.call({
method: "erpnext.stock.doctype.material_request.material_request.make_in_transit_stock_entry",
args: {
source_name: frm.doc.name,
in_transit_warehouse: values.in_transit_warehouse
},
callback: function(r) {
if (r.message) {
let doc = frappe.model.sync(r.message);
frappe.set_route('Form', doc[0].doctype, doc[0].name);
}
}
})
},
__('In Transit Transfer'),
__("Create Stock Entry")
)
},
create_pick_list: (frm) => {
frappe.model.open_mapped_doc({
method: "erpnext.stock.doctype.material_request.material_request.create_pick_list",

View File

@@ -716,3 +716,14 @@ def create_pick_list(source_name, target_doc=None):
doc.set_item_locations()
return doc
@frappe.whitelist()
def make_in_transit_stock_entry(source_name, in_transit_warehouse):
ste_doc = make_stock_entry(source_name)
ste_doc.add_to_transit = 1
for row in ste_doc.items:
row.t_warehouse = in_transit_warehouse
return ste_doc

View File

@@ -11,6 +11,7 @@ from frappe.utils import flt, today
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.material_request.material_request import (
make_in_transit_stock_entry,
make_purchase_order,
make_stock_entry,
make_supplier_quotation,
@@ -56,6 +57,22 @@ class TestMaterialRequest(FrappeTestCase):
self.assertEqual(se.doctype, "Stock Entry")
self.assertEqual(len(se.get("items")), len(mr.get("items")))
def test_in_transit_make_stock_entry(self):
mr = frappe.copy_doc(test_records[0]).insert()
self.assertRaises(frappe.ValidationError, make_stock_entry, mr.name)
mr = frappe.get_doc("Material Request", mr.name)
mr.material_request_type = "Material Transfer"
mr.submit()
in_transit_warehouse = get_in_transit_warehouse(mr.company)
se = make_in_transit_stock_entry(mr.name, in_transit_warehouse)
self.assertEqual(se.doctype, "Stock Entry")
for row in se.get("items"):
self.assertEqual(row.t_warehouse, in_transit_warehouse)
def _insert_stock_entry(self, qty1, qty2, warehouse=None):
se = frappe.get_doc(
{
@@ -742,6 +759,36 @@ class TestMaterialRequest(FrappeTestCase):
self.assertEqual(existing_requested_qty, current_requested_qty)
def get_in_transit_warehouse(company):
if not frappe.db.exists("Warehouse Type", "Transit"):
frappe.get_doc(
{
"doctype": "Warehouse Type",
"name": "Transit",
}
).insert()
in_transit_warehouse = frappe.db.exists(
"Warehouse", {"warehouse_type": "Transit", "company": company}
)
if not in_transit_warehouse:
in_transit_warehouse = (
frappe.get_doc(
{
"doctype": "Warehouse",
"warehouse_name": "Transit",
"warehouse_type": "Transit",
"company": company,
}
)
.insert()
.name
)
return in_transit_warehouse
def make_material_request(**args):
args = frappe._dict(args)
mr = frappe.new_doc("Material Request")

View File

@@ -31,5 +31,21 @@
"enabled": 1,
"price_list_name": "_Test Price List Rest of the World",
"selling": 1
},
{
"buying": 0,
"currency": "USD",
"doctype": "Price List",
"enabled": 1,
"price_list_name": "_Test Selling Price List",
"selling": 1
},
{
"buying": 1,
"currency": "USD",
"doctype": "Price List",
"enabled": 1,
"price_list_name": "_Test Buying Price List",
"selling": 0
}
]

View File

@@ -88,8 +88,15 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru
update_party_blanket_order(args, out)
# Never try to find a customer price if customer is set in these Doctype
current_customer = args.customer
if args.get("doctype") in ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]:
args.customer = None
out.update(get_price_list_rate(args, item))
args.customer = current_customer
if args.customer and cint(args.is_pos):
out.update(get_pos_profile_item_details(args.company, args, update_data=True))

View File

@@ -121,7 +121,7 @@ def get_reserved_qty(item_code, warehouse):
and parenttype='Sales Order'
and item_code != parent_item
and exists (select * from `tabSales Order` so
where name = dnpi_in.parent and docstatus = 1 and status != 'Closed')
where name = dnpi_in.parent and docstatus = 1 and status not in ('On Hold', 'Closed'))
) dnpi)
union
(select stock_qty as dnpi_qty, qty as so_item_qty,
@@ -131,7 +131,7 @@ def get_reserved_qty(item_code, warehouse):
and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0)
and exists(select * from `tabSales Order` so
where so.name = so_item.parent and so.docstatus = 1
and so.status != 'Closed'))
and so.status not in ('On Hold', 'Closed')))
) tab
where
so_item_qty >= so_item_delivered_qty

View File

@@ -0,0 +1,40 @@
import json
import frappe
from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase
from erpnext.stock.get_item_details import get_item_details
test_ignore = ["BOM"]
test_dependencies = ["Customer", "Supplier", "Item", "Price List", "Item Price"]
class TestGetItemDetail(FrappeTestCase):
def setUp(self):
make_test_records("Price List")
super().setUp()
def test_get_item_detail_purchase_order(self):
args = frappe._dict(
{
"item_code": "_Test Item",
"company": "_Test Company",
"customer": "_Test Customer",
"conversion_rate": 1.0,
"price_list_currency": "USD",
"plc_conversion_rate": 1.0,
"doctype": "Purchase Order",
"name": None,
"supplier": "_Test Supplier",
"transaction_date": None,
"conversion_rate": 1.0,
"price_list": "_Test Buying Price List",
"is_subcontracted": 0,
"ignore_pricing_rule": 1,
"qty": 1,
}
)
details = get_item_details(args)
self.assertEqual(details.get("price_list_rate"), 100)

View File

@@ -51,13 +51,31 @@ frappe.ui.form.on('Subcontracting Receipt', {
}
}));
frm.set_query("expense_account", "items", function () {
frm.set_query('expense_account', 'items', function () {
return {
query: "erpnext.controllers.queries.get_expense_account",
query: 'erpnext.controllers.queries.get_expense_account',
filters: { 'company': frm.doc.company }
};
});
frm.set_query('batch_no', 'items', function(doc, cdt, cdn) {
var row = locals[cdt][cdn];
return {
filters: {
item: row.item_code
}
}
});
let batch_no_field = frm.get_docfield("items", "batch_no");
if (batch_no_field) {
batch_no_field.get_route_options_for_new_doc = function(row) {
return {
"item": row.doc.item_code
}
};
}
frappe.db.get_single_value('Buying Settings', 'backflush_raw_materials_of_subcontract_based_on').then(val => {
if (val == 'Material Transferred for Subcontract') {
frm.fields_dict['supplied_items'].grid.grid_rows.forEach((grid_row) => {
@@ -73,7 +91,7 @@ frappe.ui.form.on('Subcontracting Receipt', {
refresh: (frm) => {
if (frm.doc.docstatus > 0) {
frm.add_custom_button(__("Stock Ledger"), function () {
frm.add_custom_button(__('Stock Ledger'), function () {
frappe.route_options = {
voucher_no: frm.doc.name,
from_date: frm.doc.posting_date,
@@ -81,8 +99,8 @@ frappe.ui.form.on('Subcontracting Receipt', {
company: frm.doc.company,
show_cancelled_entries: frm.doc.docstatus === 2
};
frappe.set_route("query-report", "Stock Ledger");
}, __("View"));
frappe.set_route('query-report', 'Stock Ledger');
}, __('View'));
frm.add_custom_button(__('Accounting Ledger'), function () {
frappe.route_options = {
@@ -90,11 +108,11 @@ frappe.ui.form.on('Subcontracting Receipt', {
from_date: frm.doc.posting_date,
to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
company: frm.doc.company,
group_by: "Group by Voucher (Consolidated)",
group_by: 'Group by Voucher (Consolidated)',
show_cancelled_entries: frm.doc.docstatus === 2
};
frappe.set_route("query-report", "General Ledger");
}, __("View"));
frappe.set_route('query-report', 'General Ledger');
}, __('View'));
}
if (!frm.doc.is_return && frm.doc.docstatus == 1 && frm.doc.per_returned < 100) {
@@ -111,25 +129,25 @@ frappe.ui.form.on('Subcontracting Receipt', {
frm.add_custom_button(__('Subcontracting Order'), function () {
if (!frm.doc.supplier) {
frappe.throw({
title: __("Mandatory"),
message: __("Please Select a Supplier")
title: __('Mandatory'),
message: __('Please Select a Supplier')
});
}
erpnext.utils.map_current_doc({
method: 'erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order.make_subcontracting_receipt',
source_doctype: "Subcontracting Order",
source_doctype: 'Subcontracting Order',
target: frm,
setters: {
supplier: frm.doc.supplier,
},
get_query_filters: {
docstatus: 1,
per_received: ["<", 100],
per_received: ['<', 100],
company: frm.doc.company
}
});
}, __("Get Items From"));
}, __('Get Items From'));
}
},