Merge branch 'develop' into pos-batch-no-stock-validation
This commit is contained in:
commit
7f786e44a1
47
.github/ISSUE_TEMPLATE/bug_report.md
vendored
47
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Report a bug encountered while using ERPNext
|
|
||||||
labels: bug
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
|
|
||||||
|
|
||||||
1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
|
|
||||||
- For questions and general support, checkout the manual https://erpnext.com/docs/user/manual/en or use https://discuss.erpnext.com
|
|
||||||
- For documentation issues, refer to https://github.com/frappe/erpnext_com
|
|
||||||
2. Use the search function before creating a new issue. Duplicates will be closed and directed to
|
|
||||||
the original discussion.
|
|
||||||
3. When making a bug report, make sure you provide all required information. The easier it is for
|
|
||||||
maintainers to reproduce, the faster it'll be fixed.
|
|
||||||
4. If you think you know what the reason for the bug is, share it with us. Maybe put in a PR 😉
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Description of the issue
|
|
||||||
|
|
||||||
## Context information (for bug reports)
|
|
||||||
|
|
||||||
**Output of `bench version`**
|
|
||||||
```
|
|
||||||
(paste here)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Steps to reproduce the issue
|
|
||||||
|
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
|
|
||||||
### Observed result
|
|
||||||
|
|
||||||
### Expected result
|
|
||||||
|
|
||||||
### Stacktrace / full error message
|
|
||||||
|
|
||||||
```
|
|
||||||
(paste here)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Additional information
|
|
||||||
|
|
||||||
OS version / distribution, `ERPNext` install method, etc.
|
|
106
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
106
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: Report a bug encountered while using ERPNext
|
||||||
|
labels: ["bug"]
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
|
||||||
|
|
||||||
|
1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
|
||||||
|
- For questions and general support, checkout the [user manual](https://docs.erpnext.com/) or use [forum](https://discuss.erpnext.com)
|
||||||
|
- For documentation issues, propose edit on [documentation site](https://docs.erpnext.com/) directly.
|
||||||
|
2. When making a bug report, make sure you provide all required information. The easier it is for
|
||||||
|
maintainers to reproduce, the faster it'll be fixed.
|
||||||
|
3. If you think you know what the reason for the bug is, share it with us. Maybe put in a PR 😉
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: bug-info
|
||||||
|
attributes:
|
||||||
|
label: Information about bug
|
||||||
|
description: Also tell us, what did you expect to happen?
|
||||||
|
placeholder: Please provide as much information as possible.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Version
|
||||||
|
description: Affected versions.
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- v12
|
||||||
|
- v13
|
||||||
|
- v14
|
||||||
|
- develop
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: module
|
||||||
|
attributes:
|
||||||
|
label: Module
|
||||||
|
description: Select affected module of ERPNext.
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- accounts
|
||||||
|
- stock
|
||||||
|
- buying
|
||||||
|
- selling
|
||||||
|
- ecommerce
|
||||||
|
- manufacturing
|
||||||
|
- HR
|
||||||
|
- projects
|
||||||
|
- support
|
||||||
|
- assets
|
||||||
|
- integrations
|
||||||
|
- quality
|
||||||
|
- regional
|
||||||
|
- portal
|
||||||
|
- agriculture
|
||||||
|
- education
|
||||||
|
- non-profit
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: exact-version
|
||||||
|
attributes:
|
||||||
|
label: Version
|
||||||
|
description: Share exact version number of Frappe and ERPNext you are using.
|
||||||
|
placeholder: |
|
||||||
|
Frappe version -
|
||||||
|
ERPNext Verion -
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: install-method
|
||||||
|
attributes:
|
||||||
|
label: Installation method
|
||||||
|
options:
|
||||||
|
- docker
|
||||||
|
- easy-install
|
||||||
|
- manual install
|
||||||
|
- FrappeCloud
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Relevant log output / Stack trace / Full Error Message.
|
||||||
|
description: Please copy and paste any relevant log output. This will be automatically formatted.
|
||||||
|
render: shell
|
||||||
|
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: terms
|
||||||
|
attributes:
|
||||||
|
label: Code of Conduct
|
||||||
|
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/frappe/erpnext/blob/develop/CODE_OF_CONDUCT.md)
|
||||||
|
options:
|
||||||
|
- label: I agree to follow this project's Code of Conduct
|
||||||
|
required: true
|
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,7 +1,10 @@
|
|||||||
---
|
---
|
||||||
name: Feature request
|
name: Feature request
|
||||||
about: Suggest an idea to improve ERPNext
|
about: Suggest an idea to improve ERPNext
|
||||||
|
title: ''
|
||||||
labels: feature-request
|
labels: feature-request
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
name: Question about using ERPNext
|
|
||||||
about: This is not the appropriate channel
|
|
||||||
labels: invalid
|
|
||||||
---
|
|
||||||
|
|
||||||
Please post on our forums:
|
|
||||||
|
|
||||||
for questions about using `ERPNext`: https://discuss.erpnext.com
|
|
||||||
|
|
||||||
for questions about using the `Frappe Framework`: ~~https://discuss.frappe.io~~ => [stackoverflow](https://stackoverflow.com/questions/tagged/frappe) tagged under `frappe`
|
|
||||||
|
|
||||||
for questions about using `bench`, probably the best place to start is the [bench repo](https://github.com/frappe/bench)
|
|
||||||
|
|
||||||
For documentation issues, use the [ERPNext Documentation](https://erpnext.com/docs/) or [Frappe Framework Documentation](https://frappe.io/docs/user/en) or the [developer cheetsheet](https://github.com/frappe/frappe/wiki/Developer-Cheatsheet)
|
|
||||||
|
|
||||||
> **Posts that are not bug reports or feature requests will not be addressed on this issue tracker.**
|
|
56
.github/stale.yml
vendored
56
.github/stale.yml
vendored
@ -1,34 +1,36 @@
|
|||||||
# Configuration for probot-stale - https://github.com/probot/stale
|
# Configuration for probot-stale - https://github.com/probot/stale
|
||||||
|
|
||||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
|
||||||
daysUntilStale: 15
|
|
||||||
|
|
||||||
# Number of days of inactivity before a stale Issue or Pull Request is closed.
|
|
||||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
|
||||||
daysUntilClose: 3
|
|
||||||
|
|
||||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
|
||||||
exemptLabels:
|
|
||||||
- hotfix
|
|
||||||
|
|
||||||
# Set to true to ignore issues in a project (defaults to false)
|
|
||||||
exemptProjects: false
|
|
||||||
|
|
||||||
# Set to true to ignore issues in a milestone (defaults to false)
|
|
||||||
exemptMilestones: true
|
|
||||||
|
|
||||||
# Label to use when marking as stale
|
# Label to use when marking as stale
|
||||||
staleLabel: inactive
|
staleLabel: inactive
|
||||||
|
|
||||||
# Comment to post when marking as stale. Set to `false` to disable
|
|
||||||
markComment: >
|
|
||||||
This pull request has been automatically marked as stale because it has not had
|
|
||||||
recent activity. It will be closed within a week if no further activity occurs, but it
|
|
||||||
only takes a comment to keep a contribution alive :) Also, even if it is closed,
|
|
||||||
you can always reopen the PR when you're ready. Thank you for contributing.
|
|
||||||
|
|
||||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||||
limitPerRun: 30
|
limitPerRun: 10
|
||||||
|
|
||||||
# Limit to only `issues` or `pulls`
|
# Set to true to ignore issues in a project (defaults to false)
|
||||||
only: pulls
|
exemptProjects: true
|
||||||
|
|
||||||
|
# Set to true to ignore issues in a milestone (defaults to false)
|
||||||
|
exemptMilestones: true
|
||||||
|
|
||||||
|
pulls:
|
||||||
|
daysUntilStale: 15
|
||||||
|
daysUntilClose: 3
|
||||||
|
exemptLabels:
|
||||||
|
- hotfix
|
||||||
|
markComment: >
|
||||||
|
This pull request has been automatically marked as inactive because it has
|
||||||
|
not had recent activity. It will be closed within 3 days if no further
|
||||||
|
activity occurs, but it only takes a comment to keep a contribution alive
|
||||||
|
:) Also, even if it is closed, you can always reopen the PR when you're
|
||||||
|
ready. Thank you for contributing.
|
||||||
|
|
||||||
|
issues:
|
||||||
|
daysUntilStale: 60
|
||||||
|
daysUntilClose: 7
|
||||||
|
exemptLabels:
|
||||||
|
- valid
|
||||||
|
- to-validate
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as inactive because it has not had
|
||||||
|
recent activity and it wasn't validated by maintainer team. It will be
|
||||||
|
closed within a week if no further activity occurs.
|
||||||
|
10
codecov.yml
10
codecov.yml
@ -8,6 +8,16 @@ coverage:
|
|||||||
target: auto
|
target: auto
|
||||||
threshold: 0.5%
|
threshold: 0.5%
|
||||||
|
|
||||||
|
patch:
|
||||||
|
default:
|
||||||
|
target: 85%
|
||||||
|
threshold: 0%
|
||||||
|
base: auto
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
if_ci_failed: ignore
|
||||||
|
only_pulls: true
|
||||||
|
|
||||||
comment:
|
comment:
|
||||||
layout: "diff, files"
|
layout: "diff, files"
|
||||||
require_changes: true
|
require_changes: true
|
||||||
|
1
dev-requirements.txt
Normal file
1
dev-requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
hypothesis~=6.31.0
|
@ -55,9 +55,9 @@ def set_perpetual_inventory(enable=1, company=None):
|
|||||||
company.enable_perpetual_inventory = enable
|
company.enable_perpetual_inventory = enable
|
||||||
company.save()
|
company.save()
|
||||||
|
|
||||||
def encode_company_abbr(name, company):
|
def encode_company_abbr(name, company=None, abbr=None):
|
||||||
'''Returns name encoded with company abbreviation'''
|
'''Returns name encoded with company abbreviation'''
|
||||||
company_abbr = frappe.get_cached_value('Company', company, "abbr")
|
company_abbr = abbr or frappe.get_cached_value('Company', company, "abbr")
|
||||||
parts = name.rsplit(" - ", 1)
|
parts = name.rsplit(" - ", 1)
|
||||||
|
|
||||||
if parts[-1].lower() != company_abbr.lower():
|
if parts[-1].lower() != company_abbr.lower():
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
QUnit.module('accounts');
|
|
||||||
|
|
||||||
QUnit.test("test account", function(assert) {
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => frappe.set_route('Tree', 'Account'),
|
|
||||||
() => frappe.timeout(3),
|
|
||||||
() => frappe.click_button('Expand All'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_link('Debtors'),
|
|
||||||
() => frappe.click_button('Edit'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.root_type=='Asset');
|
|
||||||
assert.ok(cur_frm.doc.report_type=='Balance Sheet');
|
|
||||||
assert.ok(cur_frm.doc.account_type=='Receivable');
|
|
||||||
},
|
|
||||||
() => frappe.click_button('Ledger'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
// check if general ledger report shown
|
|
||||||
assert.deepEqual(frappe.get_route(), ['query-report', 'General Ledger']);
|
|
||||||
window.history.back();
|
|
||||||
return frappe.timeout(1);
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,69 +0,0 @@
|
|||||||
QUnit.module('accounts');
|
|
||||||
|
|
||||||
QUnit.test("test account with number", function(assert) {
|
|
||||||
assert.expect(7);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => frappe.set_route('Tree', 'Account'),
|
|
||||||
() => frappe.click_link('Income'),
|
|
||||||
() => frappe.click_button('Add Child'),
|
|
||||||
() => frappe.timeout(.5),
|
|
||||||
() => {
|
|
||||||
cur_dialog.fields_dict.account_name.$input.val("Test Income");
|
|
||||||
cur_dialog.fields_dict.account_number.$input.val("4010");
|
|
||||||
},
|
|
||||||
() => frappe.click_button('Create New'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
assert.ok($('a:contains("4010 - Test Income"):visible').length!=0, "Account created with number");
|
|
||||||
},
|
|
||||||
() => frappe.click_link('4010 - Test Income'),
|
|
||||||
() => frappe.click_button('Edit'),
|
|
||||||
() => frappe.timeout(.5),
|
|
||||||
() => frappe.click_button('Update Account Number'),
|
|
||||||
() => frappe.timeout(.5),
|
|
||||||
() => {
|
|
||||||
cur_dialog.fields_dict.account_number.$input.val("4020");
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => cur_dialog.primary_action(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => cur_frm.refresh_fields(),
|
|
||||||
() => frappe.timeout(.5),
|
|
||||||
() => {
|
|
||||||
var abbr = frappe.get_abbr(frappe.defaults.get_default("Company"));
|
|
||||||
var new_account = "4020 - Test Income - " + abbr;
|
|
||||||
assert.ok(cur_frm.doc.name==new_account, "Account renamed");
|
|
||||||
assert.ok(cur_frm.doc.account_name=="Test Income", "account name remained same");
|
|
||||||
assert.ok(cur_frm.doc.account_number=="4020", "Account number updated to 4020");
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Menu'),
|
|
||||||
() => frappe.click_link('Rename'),
|
|
||||||
() => frappe.timeout(.5),
|
|
||||||
() => {
|
|
||||||
cur_dialog.fields_dict.new_name.$input.val("4030 - Test Income");
|
|
||||||
},
|
|
||||||
() => frappe.timeout(.5),
|
|
||||||
() => frappe.click_button("Rename"),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.account_name=="Test Income", "account name remained same");
|
|
||||||
assert.ok(cur_frm.doc.account_number=="4030", "Account number updated to 4030");
|
|
||||||
},
|
|
||||||
() => frappe.timeout(.5),
|
|
||||||
() => frappe.click_button('Chart of Accounts'),
|
|
||||||
() => frappe.timeout(.5),
|
|
||||||
() => frappe.click_button('Menu'),
|
|
||||||
() => frappe.click_link('Refresh'),
|
|
||||||
() => frappe.click_button('Expand All'),
|
|
||||||
() => frappe.click_link('4030 - Test Income'),
|
|
||||||
() => frappe.click_button('Delete'),
|
|
||||||
() => frappe.click_button('Yes'),
|
|
||||||
() => frappe.timeout(.5),
|
|
||||||
() => {
|
|
||||||
assert.ok($('a:contains("4030 - Test Account"):visible').length==0, "Account deleted");
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,46 +0,0 @@
|
|||||||
QUnit.module('accounts');
|
|
||||||
QUnit.test("test account", assert => {
|
|
||||||
assert.expect(3);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => frappe.set_route('Tree', 'Account'),
|
|
||||||
() => frappe.click_button('Expand All'),
|
|
||||||
() => frappe.click_link('Duties and Taxes - '+ frappe.get_abbr(frappe.defaults.get_default("Company"))),
|
|
||||||
() => {
|
|
||||||
if($('a:contains("CGST"):visible').length == 0){
|
|
||||||
return frappe.map_tax.make('CGST', 9);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
if($('a:contains("SGST"):visible').length == 0){
|
|
||||||
return frappe.map_tax.make('SGST', 9);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
if($('a:contains("IGST"):visible').length == 0){
|
|
||||||
return frappe.map_tax.make('IGST', 18);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
assert.ok($('a:contains("CGST"):visible').length!=0, "CGST Checked");
|
|
||||||
assert.ok($('a:contains("SGST"):visible').length!=0, "SGST Checked");
|
|
||||||
assert.ok($('a:contains("IGST"):visible').length!=0, "IGST Checked");
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
frappe.map_tax = {
|
|
||||||
make:function(text,rate){
|
|
||||||
return frappe.run_serially([
|
|
||||||
() => frappe.click_button('Add Child'),
|
|
||||||
() => frappe.timeout(0.2),
|
|
||||||
() => cur_dialog.set_value('account_name',text),
|
|
||||||
() => cur_dialog.set_value('account_type','Tax'),
|
|
||||||
() => cur_dialog.set_value('tax_rate',rate),
|
|
||||||
() => cur_dialog.set_value('account_currency','INR'),
|
|
||||||
() => frappe.click_button('Create New'),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
};
|
|
@ -10,6 +10,8 @@ from frappe.custom.doctype.property_setter.property_setter import make_property_
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import cint
|
from frappe.utils import cint
|
||||||
|
|
||||||
|
from erpnext.stock.utils import check_pending_reposting
|
||||||
|
|
||||||
|
|
||||||
class AccountsSettings(Document):
|
class AccountsSettings(Document):
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
@ -25,6 +27,7 @@ class AccountsSettings(Document):
|
|||||||
self.validate_stale_days()
|
self.validate_stale_days()
|
||||||
self.enable_payment_schedule_in_print()
|
self.enable_payment_schedule_in_print()
|
||||||
self.toggle_discount_accounting_fields()
|
self.toggle_discount_accounting_fields()
|
||||||
|
self.validate_pending_reposts()
|
||||||
|
|
||||||
def validate_stale_days(self):
|
def validate_stale_days(self):
|
||||||
if not self.allow_stale and cint(self.stale_days) <= 0:
|
if not self.allow_stale and cint(self.stale_days) <= 0:
|
||||||
@ -56,3 +59,8 @@ class AccountsSettings(Document):
|
|||||||
make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
|
make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
|
||||||
|
|
||||||
make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
|
make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_pending_reposts(self):
|
||||||
|
if self.acc_frozen_upto:
|
||||||
|
check_pending_reposting(self.acc_frozen_upto)
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
QUnit.module('accounts');
|
|
||||||
|
|
||||||
QUnit.test("test: Accounts Settings doesn't allow negatives", function (assert) {
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
assert.expect(2);
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => frappe.set_route('Form', 'Accounts Settings', 'Accounts Settings'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => unchecked_if_checked(cur_frm, 'Allow Stale Exchange Rates', frappe.click_check),
|
|
||||||
() => cur_frm.set_value('stale_days', 0),
|
|
||||||
() => frappe.click_button('Save'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_dialog);
|
|
||||||
},
|
|
||||||
() => frappe.click_button('Close'),
|
|
||||||
() => cur_frm.set_value('stale_days', -1),
|
|
||||||
() => frappe.click_button('Save'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_dialog);
|
|
||||||
},
|
|
||||||
() => frappe.click_button('Close'),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
const unchecked_if_checked = function(frm, field_name, fn){
|
|
||||||
if (frm.doc.allow_stale) {
|
|
||||||
return fn(field_name);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,39 +0,0 @@
|
|||||||
QUnit.module('Journal Entry');
|
|
||||||
|
|
||||||
QUnit.test("test journal entry", function(assert) {
|
|
||||||
assert.expect(2);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Journal Entry', [
|
|
||||||
{posting_date:frappe.datetime.add_days(frappe.datetime.nowdate(), 0)},
|
|
||||||
{accounts: [
|
|
||||||
[
|
|
||||||
{'account':'Debtors - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
|
|
||||||
{'party_type':'Customer'},
|
|
||||||
{'party':'Test Customer 1'},
|
|
||||||
{'credit_in_account_currency':1000},
|
|
||||||
{'is_advance':'Yes'},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{'account':'HDFC - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
|
|
||||||
{'debit_in_account_currency':1000},
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{cheque_no:1234},
|
|
||||||
{cheque_date: frappe.datetime.add_days(frappe.datetime.nowdate(), -1)},
|
|
||||||
{user_remark: 'Test'},
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => {
|
|
||||||
// get_item_details
|
|
||||||
assert.ok(cur_frm.doc.total_debit==1000, "total debit correct");
|
|
||||||
assert.ok(cur_frm.doc.total_credit==1000, "total credit correct");
|
|
||||||
},
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -159,7 +159,8 @@ class OpeningInvoiceCreationTool(Document):
|
|||||||
frappe.scrub(row.party_type): row.party,
|
frappe.scrub(row.party_type): row.party,
|
||||||
"is_pos": 0,
|
"is_pos": 0,
|
||||||
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
|
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
|
||||||
"update_stock": 0
|
"update_stock": 0,
|
||||||
|
"invoice_number": row.invoice_number
|
||||||
})
|
})
|
||||||
|
|
||||||
accounting_dimension = get_accounting_dimensions()
|
accounting_dimension = get_accounting_dimensions()
|
||||||
@ -200,10 +201,13 @@ def start_import(invoices):
|
|||||||
names = []
|
names = []
|
||||||
for idx, d in enumerate(invoices):
|
for idx, d in enumerate(invoices):
|
||||||
try:
|
try:
|
||||||
|
invoice_number = None
|
||||||
|
if d.invoice_number:
|
||||||
|
invoice_number = d.invoice_number
|
||||||
publish(idx, len(invoices), d.doctype)
|
publish(idx, len(invoices), d.doctype)
|
||||||
doc = frappe.get_doc(d)
|
doc = frappe.get_doc(d)
|
||||||
doc.flags.ignore_mandatory = True
|
doc.flags.ignore_mandatory = True
|
||||||
doc.insert()
|
doc.insert(set_name=invoice_number)
|
||||||
doc.submit()
|
doc.submit()
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
names.append(doc.name)
|
names.append(doc.name)
|
||||||
|
@ -18,10 +18,10 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
|||||||
if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
|
if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
|
||||||
make_company()
|
make_company()
|
||||||
|
|
||||||
def make_invoices(self, invoice_type="Sales", company=None, party_1=None, party_2=None):
|
def make_invoices(self, invoice_type="Sales", company=None, party_1=None, party_2=None, invoice_number=None):
|
||||||
doc = frappe.get_single("Opening Invoice Creation Tool")
|
doc = frappe.get_single("Opening Invoice Creation Tool")
|
||||||
args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company,
|
args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company,
|
||||||
party_1=party_1, party_2=party_2)
|
party_1=party_1, party_2=party_2, invoice_number=invoice_number)
|
||||||
doc.update(args)
|
doc.update(args)
|
||||||
return doc.make_invoices()
|
return doc.make_invoices()
|
||||||
|
|
||||||
@ -92,6 +92,20 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
|||||||
# teardown
|
# teardown
|
||||||
frappe.db.set_value("Company", company, "default_receivable_account", old_default_receivable_account)
|
frappe.db.set_value("Company", company, "default_receivable_account", old_default_receivable_account)
|
||||||
|
|
||||||
|
def test_renaming_of_invoice_using_invoice_number_field(self):
|
||||||
|
company = "_Test Opening Invoice Company"
|
||||||
|
party_1, party_2 = make_customer("Customer A"), make_customer("Customer B")
|
||||||
|
self.make_invoices(company=company, party_1=party_1, party_2=party_2, invoice_number="TEST-NEW-INV-11")
|
||||||
|
|
||||||
|
sales_inv1 = frappe.get_all('Sales Invoice', filters={'customer':'Customer A'})[0].get("name")
|
||||||
|
sales_inv2 = frappe.get_all('Sales Invoice', filters={'customer':'Customer B'})[0].get("name")
|
||||||
|
self.assertEqual(sales_inv1, "TEST-NEW-INV-11")
|
||||||
|
|
||||||
|
#teardown
|
||||||
|
for inv in [sales_inv1, sales_inv2]:
|
||||||
|
doc = frappe.get_doc('Sales Invoice', inv)
|
||||||
|
doc.cancel()
|
||||||
|
|
||||||
def get_opening_invoice_creation_dict(**args):
|
def get_opening_invoice_creation_dict(**args):
|
||||||
party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
|
party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
|
||||||
company = args.get("company", "_Test Company")
|
company = args.get("company", "_Test Company")
|
||||||
@ -107,7 +121,8 @@ def get_opening_invoice_creation_dict(**args):
|
|||||||
"item_name": "Opening Item",
|
"item_name": "Opening Item",
|
||||||
"due_date": "2016-09-10",
|
"due_date": "2016-09-10",
|
||||||
"posting_date": "2016-09-05",
|
"posting_date": "2016-09-05",
|
||||||
"temporary_opening_account": get_temporary_opening_account(company)
|
"temporary_opening_account": get_temporary_opening_account(company),
|
||||||
|
"invoice_number": args.get("invoice_number")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"qty": 2.0,
|
"qty": 2.0,
|
||||||
@ -116,7 +131,8 @@ def get_opening_invoice_creation_dict(**args):
|
|||||||
"item_name": "Opening Item",
|
"item_name": "Opening Item",
|
||||||
"due_date": "2016-09-10",
|
"due_date": "2016-09-10",
|
||||||
"posting_date": "2016-09-05",
|
"posting_date": "2016-09-05",
|
||||||
"temporary_opening_account": get_temporary_opening_account(company)
|
"temporary_opening_account": get_temporary_opening_account(company),
|
||||||
|
"invoice_number": None
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@ -132,7 +148,7 @@ def make_company():
|
|||||||
company.company_name = "_Test Opening Invoice Company"
|
company.company_name = "_Test Opening Invoice Company"
|
||||||
company.abbr = "_TOIC"
|
company.abbr = "_TOIC"
|
||||||
company.default_currency = "INR"
|
company.default_currency = "INR"
|
||||||
company.country = "India"
|
company.country = "Pakistan"
|
||||||
company.insert()
|
company.insert()
|
||||||
return company
|
return company
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"creation": "2017-08-29 04:26:36.159247",
|
"creation": "2017-08-29 04:26:36.159247",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
|
"invoice_number",
|
||||||
"party_type",
|
"party_type",
|
||||||
"party",
|
"party",
|
||||||
"temporary_opening_account",
|
"temporary_opening_account",
|
||||||
@ -103,10 +105,18 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "dimension_col_break",
|
"fieldname": "dimension_col_break",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Reference number of the invoice from the previous system",
|
||||||
|
"fieldname": "invoice_number",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Invoice Number"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-07-25 15:00:00.460695",
|
"links": [],
|
||||||
|
"modified": "2021-12-17 19:25:06.053187",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Opening Invoice Creation Tool Item",
|
"name": "Opening Invoice Creation Tool Item",
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
QUnit.module('Payment Entry');
|
|
||||||
|
|
||||||
QUnit.test("test payment entry", function(assert) {
|
|
||||||
assert.expect(6);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Sales Invoice', [
|
|
||||||
{customer: 'Test Customer 1'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'item_code': 'Test Product 1'},
|
|
||||||
{'qty': 1},
|
|
||||||
{'rate': 101},
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Close'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Make'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_link('Payment'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
assert.equal(frappe.get_route()[1], 'Payment Entry',
|
|
||||||
'made payment entry');
|
|
||||||
assert.equal(cur_frm.doc.party, 'Test Customer 1',
|
|
||||||
'customer set in payment entry');
|
|
||||||
assert.equal(cur_frm.doc.paid_amount, 101,
|
|
||||||
'paid amount set in payment entry');
|
|
||||||
assert.equal(cur_frm.doc.references[0].allocated_amount, 101,
|
|
||||||
'amount allocated against sales invoice');
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => cur_frm.set_value('paid_amount', 100),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
frappe.model.set_value("Payment Entry Reference", cur_frm.doc.references[0].name,
|
|
||||||
"allocated_amount", 101);
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Write Off Difference Amount'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
assert.equal(cur_frm.doc.difference_amount, 0, 'difference amount is zero');
|
|
||||||
assert.equal(cur_frm.doc.deductions[0].amount, 1, 'Write off amount = 1');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,60 +0,0 @@
|
|||||||
QUnit.module('Payment Entry');
|
|
||||||
|
|
||||||
QUnit.test("test payment entry", function(assert) {
|
|
||||||
assert.expect(7 );
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Purchase Invoice', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{bill_no: 'in1234'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'qty': 2},
|
|
||||||
{'item_code': 'Test Product 1'},
|
|
||||||
{'rate':1000},
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{update_stock:1},
|
|
||||||
{supplier_address: 'Test1-Billing'},
|
|
||||||
{contact_person: 'Contact 3-Test Supplier'},
|
|
||||||
{tc_name: 'Test Term 1'},
|
|
||||||
{terms: 'This is just a Test'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Make'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => frappe.click_link('Payment'),
|
|
||||||
() => frappe.timeout(3),
|
|
||||||
() => cur_frm.set_value('mode_of_payment','Cash'),
|
|
||||||
() => frappe.timeout(3),
|
|
||||||
() => {
|
|
||||||
assert.equal(frappe.get_route()[1], 'Payment Entry',
|
|
||||||
'made payment entry');
|
|
||||||
assert.equal(cur_frm.doc.party, 'Test Supplier',
|
|
||||||
'supplier set in payment entry');
|
|
||||||
assert.equal(cur_frm.doc.paid_amount, 2000,
|
|
||||||
'paid amount set in payment entry');
|
|
||||||
assert.equal(cur_frm.doc.references[0].allocated_amount, 2000,
|
|
||||||
'amount allocated against purchase invoice');
|
|
||||||
assert.equal(cur_frm.doc.references[0].bill_no, 'in1234',
|
|
||||||
'invoice number allocated against purchase invoice');
|
|
||||||
assert.equal(cur_frm.get_field('total_allocated_amount').value, 2000,
|
|
||||||
'correct amount allocated in Write Off');
|
|
||||||
assert.equal(cur_frm.get_field('unallocated_amount').value, 0,
|
|
||||||
'correct amount unallocated in Write Off');
|
|
||||||
},
|
|
||||||
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,28 +0,0 @@
|
|||||||
QUnit.module('Accounts');
|
|
||||||
|
|
||||||
QUnit.test("test payment entry", function(assert) {
|
|
||||||
assert.expect(1);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Payment Entry', [
|
|
||||||
{payment_type:'Receive'},
|
|
||||||
{mode_of_payment:'Cash'},
|
|
||||||
{party_type:'Customer'},
|
|
||||||
{party:'Test Customer 3'},
|
|
||||||
{paid_amount:675},
|
|
||||||
{reference_no:123},
|
|
||||||
{reference_date: frappe.datetime.add_days(frappe.datetime.nowdate(), 0)},
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => {
|
|
||||||
// get_item_details
|
|
||||||
assert.ok(cur_frm.doc.total_allocated_amount==675, "Allocated AmountCorrect");
|
|
||||||
},
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,67 +0,0 @@
|
|||||||
QUnit.module('Payment Entry');
|
|
||||||
|
|
||||||
QUnit.test("test payment entry", function(assert) {
|
|
||||||
assert.expect(8);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Sales Invoice', [
|
|
||||||
{customer: 'Test Customer 1'},
|
|
||||||
{company: 'For Testing'},
|
|
||||||
{currency: 'INR'},
|
|
||||||
{selling_price_list: '_Test Price List'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'qty': 1},
|
|
||||||
{'item_code': 'Test Product 1'},
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(1.5),
|
|
||||||
() => frappe.click_button('Close'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.click_button('Make'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_link('Payment'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => cur_frm.set_value("paid_to", "_Test Cash - FT"),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => {
|
|
||||||
assert.equal(frappe.get_route()[1], 'Payment Entry', 'made payment entry');
|
|
||||||
assert.equal(cur_frm.doc.party, 'Test Customer 1', 'customer set in payment entry');
|
|
||||||
assert.equal(cur_frm.doc.paid_from, 'Debtors - FT', 'customer account set in payment entry');
|
|
||||||
assert.equal(cur_frm.doc.paid_amount, 100, 'paid amount set in payment entry');
|
|
||||||
assert.equal(cur_frm.doc.references[0].allocated_amount, 100,
|
|
||||||
'amount allocated against sales invoice');
|
|
||||||
},
|
|
||||||
() => cur_frm.set_value('paid_amount', 95),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
frappe.model.set_value("Payment Entry Reference",
|
|
||||||
cur_frm.doc.references[0].name, "allocated_amount", 100);
|
|
||||||
},
|
|
||||||
() => frappe.timeout(.5),
|
|
||||||
() => {
|
|
||||||
assert.equal(cur_frm.doc.difference_amount, 5, 'difference amount is 5');
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
frappe.db.set_value("Company", "For Testing", "write_off_account", "_Test Write Off - FT");
|
|
||||||
frappe.timeout(1);
|
|
||||||
frappe.db.set_value("Company", "For Testing",
|
|
||||||
"exchange_gain_loss_account", "_Test Exchange Gain/Loss - FT");
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Write Off Difference Amount'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
assert.equal(cur_frm.doc.difference_amount, 0, 'difference amount is zero');
|
|
||||||
assert.equal(cur_frm.doc.deductions[0].amount, 5, 'Write off amount = 5');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,28 +0,0 @@
|
|||||||
QUnit.module('Pricing Rule');
|
|
||||||
|
|
||||||
QUnit.test("test pricing rule", function(assert) {
|
|
||||||
assert.expect(2);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make("Pricing Rule", [
|
|
||||||
{title: 'Test Pricing Rule'},
|
|
||||||
{item_code:'Test Product 2'},
|
|
||||||
{selling:1},
|
|
||||||
{applicable_for:'Customer'},
|
|
||||||
{customer:'Test Customer 3'},
|
|
||||||
{currency: frappe.defaults.get_default("currency")}
|
|
||||||
{min_qty:1},
|
|
||||||
{max_qty:20},
|
|
||||||
{valid_upto: frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
|
|
||||||
{discount_percentage:10},
|
|
||||||
{for_price_list:'Standard Selling'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.item_code=='Test Product 2');
|
|
||||||
assert.ok(cur_frm.doc.customer=='Test Customer 3');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,58 +0,0 @@
|
|||||||
QUnit.module('Pricing Rule');
|
|
||||||
|
|
||||||
QUnit.test("test pricing rule with different currency", function(assert) {
|
|
||||||
assert.expect(3);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make("Pricing Rule", [
|
|
||||||
{title: 'Test Pricing Rule 2'},
|
|
||||||
{apply_on: 'Item Code'},
|
|
||||||
{item_code:'Test Product 4'},
|
|
||||||
{selling:1},
|
|
||||||
{priority: 1},
|
|
||||||
{min_qty:1},
|
|
||||||
{max_qty:20},
|
|
||||||
{valid_upto: frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
|
|
||||||
{margin_type: 'Amount'},
|
|
||||||
{margin_rate_or_amount: 20},
|
|
||||||
{rate_or_discount: 'Rate'},
|
|
||||||
{rate:200},
|
|
||||||
{currency:'USD'}
|
|
||||||
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.item_code=='Test Product 4');
|
|
||||||
},
|
|
||||||
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Sales Order', [
|
|
||||||
{customer: 'Test Customer 1'},
|
|
||||||
{currency: 'INR'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
|
|
||||||
{'qty': 5},
|
|
||||||
{'item_code': "Test Product 4"}
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => {
|
|
||||||
// get_item_details
|
|
||||||
assert.ok(cur_frm.doc.items[0].pricing_rule=='Test Pricing Rule 2', "Pricing rule correct");
|
|
||||||
// margin not applied because different currency in pricing rule
|
|
||||||
assert.ok(cur_frm.doc.items[0].margin_type==null, "Margin correct");
|
|
||||||
},
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,56 +0,0 @@
|
|||||||
QUnit.module('Pricing Rule');
|
|
||||||
|
|
||||||
QUnit.test("test pricing rule with same currency", function(assert) {
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make("Pricing Rule", [
|
|
||||||
{title: 'Test Pricing Rule 1'},
|
|
||||||
{apply_on: 'Item Code'},
|
|
||||||
{item_code:'Test Product 4'},
|
|
||||||
{selling:1},
|
|
||||||
{min_qty:1},
|
|
||||||
{max_qty:20},
|
|
||||||
{valid_upto: frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
|
|
||||||
{rate_or_discount: 'Rate'},
|
|
||||||
{rate:200},
|
|
||||||
{currency:'USD'}
|
|
||||||
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.item_code=='Test Product 4');
|
|
||||||
},
|
|
||||||
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Sales Order', [
|
|
||||||
{customer: 'Test Customer 1'},
|
|
||||||
{currency: 'USD'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
|
|
||||||
{'qty': 5},
|
|
||||||
{'item_code': "Test Product 4"}
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => {
|
|
||||||
// get_item_details
|
|
||||||
assert.ok(cur_frm.doc.items[0].pricing_rule=='Test Pricing Rule 1', "Pricing rule correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].price_list_rate==200, "Item rate correct");
|
|
||||||
// get_total
|
|
||||||
assert.ok(cur_frm.doc.total== 1000, "Total correct");
|
|
||||||
},
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -114,6 +114,9 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.set_status()
|
self.set_status()
|
||||||
self.validate_purchase_receipt_if_update_stock()
|
self.validate_purchase_receipt_if_update_stock()
|
||||||
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
|
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
|
||||||
|
self.reset_default_field_value("set_warehouse", "items", "warehouse")
|
||||||
|
self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
|
||||||
|
self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
|
||||||
|
|
||||||
def validate_release_date(self):
|
def validate_release_date(self):
|
||||||
if self.release_date and getdate(nowdate()) >= getdate(self.release_date):
|
if self.release_date and getdate(nowdate()) >= getdate(self.release_date):
|
||||||
@ -294,8 +297,15 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.expense_account = stock_not_billed_account
|
item.expense_account = stock_not_billed_account
|
||||||
|
|
||||||
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
|
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
|
||||||
item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
|
asset_category_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
|
||||||
company = self.company)
|
company = self.company)
|
||||||
|
if not asset_category_account:
|
||||||
|
form_link = get_link_to_form('Asset Category', asset_category)
|
||||||
|
throw(
|
||||||
|
_("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company),
|
||||||
|
title=_("Missing Account")
|
||||||
|
)
|
||||||
|
item.expense_account = asset_category_account
|
||||||
elif item.is_fixed_asset and item.pr_detail:
|
elif item.is_fixed_asset and item.pr_detail:
|
||||||
item.expense_account = asset_received_but_not_billed
|
item.expense_account = asset_received_but_not_billed
|
||||||
elif not item.expense_account and for_validate:
|
elif not item.expense_account and for_validate:
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
QUnit.module('Purchase Invoice');
|
|
||||||
|
|
||||||
QUnit.test("test purchase invoice", function(assert) {
|
|
||||||
assert.expect(9);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Purchase Invoice', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{bill_no: 'in123'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'qty': 5},
|
|
||||||
{'item_code': 'Test Product 1'},
|
|
||||||
{'rate':100},
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{update_stock:1},
|
|
||||||
{supplier_address: 'Test1-Billing'},
|
|
||||||
{contact_person: 'Contact 3-Test Supplier'},
|
|
||||||
{taxes_and_charges: 'TEST In State GST - FT'},
|
|
||||||
{tc_name: 'Test Term 1'},
|
|
||||||
{terms: 'This is Test'},
|
|
||||||
{payment_terms_template: '_Test Payment Term Template UI'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => {
|
|
||||||
// get_item_details
|
|
||||||
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
|
|
||||||
// get tax details
|
|
||||||
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
|
|
||||||
// get tax account head details
|
|
||||||
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
|
|
||||||
// grand_total Calculated
|
|
||||||
assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
|
|
||||||
|
|
||||||
assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct");
|
|
||||||
assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
|
|
||||||
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
let date = cur_frm.doc.due_date;
|
|
||||||
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
|
|
||||||
frappe.timeout(0.5);
|
|
||||||
assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Close'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.set_form_values(cur_frm, [{'payment_terms_schedule': ''}]),
|
|
||||||
() => {
|
|
||||||
let date = cur_frm.doc.due_date;
|
|
||||||
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
|
|
||||||
frappe.timeout(0.5);
|
|
||||||
assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Close'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.set_form_values(cur_frm, [{'payment_schedule': []}]),
|
|
||||||
() => {
|
|
||||||
let date = cur_frm.doc.due_date;
|
|
||||||
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
|
|
||||||
frappe.timeout(0.5);
|
|
||||||
assert.ok(!cur_dialog, 'Message is not shown');
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,28 +0,0 @@
|
|||||||
QUnit.module('Sales Taxes and Charges Template');
|
|
||||||
|
|
||||||
QUnit.test("test sales taxes and charges template", function(assert) {
|
|
||||||
assert.expect(2);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Purchase Taxes and Charges Template', [
|
|
||||||
{title: "TEST In State GST"},
|
|
||||||
{taxes:[
|
|
||||||
[
|
|
||||||
{charge_type:"On Net Total"},
|
|
||||||
{account_head:"CGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{charge_type:"On Net Total"},
|
|
||||||
{account_head:"SGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.title=='TEST In State GST');
|
|
||||||
assert.ok(cur_frm.doc.name=='TEST In State GST - FT');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -155,6 +155,8 @@ class SalesInvoice(SellingController):
|
|||||||
if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points and not self.is_consolidated:
|
if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points and not self.is_consolidated:
|
||||||
validate_loyalty_points(self, self.loyalty_points)
|
validate_loyalty_points(self, self.loyalty_points)
|
||||||
|
|
||||||
|
self.reset_default_field_value("set_warehouse", "items", "warehouse")
|
||||||
|
|
||||||
def validate_fixed_asset(self):
|
def validate_fixed_asset(self):
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
|
if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
|
||||||
@ -1047,6 +1049,8 @@ class SalesInvoice(SellingController):
|
|||||||
frappe.flags.is_reverse_depr_entry = False
|
frappe.flags.is_reverse_depr_entry = False
|
||||||
asset.flags.ignore_validate_update_after_submit = True
|
asset.flags.ignore_validate_update_after_submit = True
|
||||||
schedule.journal_entry = None
|
schedule.journal_entry = None
|
||||||
|
depreciation_amount = self.get_depreciation_amount_in_je(reverse_journal_entry)
|
||||||
|
asset.finance_books[0].value_after_depreciation += depreciation_amount
|
||||||
asset.save()
|
asset.save()
|
||||||
|
|
||||||
def get_posting_date_of_sales_invoice(self):
|
def get_posting_date_of_sales_invoice(self):
|
||||||
@ -1069,6 +1073,12 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_depreciation_amount_in_je(self, journal_entry):
|
||||||
|
if journal_entry.accounts[0].debit_in_account_currency:
|
||||||
|
return journal_entry.accounts[0].debit_in_account_currency
|
||||||
|
else:
|
||||||
|
return journal_entry.accounts[0].credit_in_account_currency
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enable_discount_accounting(self):
|
def enable_discount_accounting(self):
|
||||||
if not hasattr(self, "_enable_discount_accounting"):
|
if not hasattr(self, "_enable_discount_accounting"):
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
QUnit.module('Sales Invoice');
|
|
||||||
|
|
||||||
QUnit.test("test sales Invoice", function(assert) {
|
|
||||||
assert.expect(9);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Sales Invoice', [
|
|
||||||
{customer: 'Test Customer 1'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'qty': 5},
|
|
||||||
{'item_code': 'Test Product 1'},
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{update_stock:1},
|
|
||||||
{customer_address: 'Test1-Billing'},
|
|
||||||
{shipping_address_name: 'Test1-Shipping'},
|
|
||||||
{contact_person: 'Contact 1-Test Customer 1'},
|
|
||||||
{taxes_and_charges: 'TEST In State GST - FT'},
|
|
||||||
{tc_name: 'Test Term 1'},
|
|
||||||
{terms: 'This is Test'},
|
|
||||||
{payment_terms_template: '_Test Payment Term Template UI'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => {
|
|
||||||
// get_item_details
|
|
||||||
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
|
|
||||||
// get tax details
|
|
||||||
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
|
|
||||||
// get tax account head details
|
|
||||||
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
|
|
||||||
// grand_total Calculated
|
|
||||||
assert.ok(cur_frm.doc.grand_total==590, "Grand Total correct");
|
|
||||||
|
|
||||||
assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct");
|
|
||||||
assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
|
|
||||||
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
let date = cur_frm.doc.due_date;
|
|
||||||
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
|
|
||||||
frappe.timeout(0.5);
|
|
||||||
assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Close'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.set_form_values(cur_frm, [{'payment_terms_schedule': ''}]),
|
|
||||||
() => {
|
|
||||||
let date = cur_frm.doc.due_date;
|
|
||||||
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
|
|
||||||
frappe.timeout(0.5);
|
|
||||||
assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Close'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.set_form_values(cur_frm, [{'payment_schedule': []}]),
|
|
||||||
() => {
|
|
||||||
let date = cur_frm.doc.due_date;
|
|
||||||
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
|
|
||||||
frappe.timeout(0.5);
|
|
||||||
assert.ok(!cur_dialog, 'Message is not shown');
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,42 +0,0 @@
|
|||||||
QUnit.module('Sales Invoice');
|
|
||||||
|
|
||||||
QUnit.test("test sales Invoice", function(assert) {
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Sales Invoice', [
|
|
||||||
{customer: 'Test Customer 1'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'qty': 5},
|
|
||||||
{'item_code': 'Test Product 1'},
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{update_stock:1},
|
|
||||||
{customer_address: 'Test1-Billing'},
|
|
||||||
{shipping_address_name: 'Test1-Shipping'},
|
|
||||||
{contact_person: 'Contact 1-Test Customer 1'},
|
|
||||||
{taxes_and_charges: 'TEST In State GST - FT'},
|
|
||||||
{tc_name: 'Test Term 1'},
|
|
||||||
{terms: 'This is Test'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => {
|
|
||||||
// get_item_details
|
|
||||||
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
|
|
||||||
// get tax details
|
|
||||||
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
|
|
||||||
// get tax account head details
|
|
||||||
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
|
|
||||||
// grand_total Calculated
|
|
||||||
assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
|
|
||||||
|
|
||||||
},
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,35 +0,0 @@
|
|||||||
QUnit.module('Accounts');
|
|
||||||
|
|
||||||
QUnit.test("test sales invoice with margin", function(assert) {
|
|
||||||
assert.expect(3);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Sales Invoice', [
|
|
||||||
{customer: 'Test Customer 1'},
|
|
||||||
{selling_price_list: 'Test-Selling-USD'},
|
|
||||||
{currency: 'USD'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'item_code': 'Test Product 4'},
|
|
||||||
{'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)},
|
|
||||||
{'qty': 1},
|
|
||||||
{'margin_type': 'Percentage'},
|
|
||||||
{'margin_rate_or_amount': 20}
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.items[0].rate_with_margin == 240, "Margin rate correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].base_rate_with_margin == cur_frm.doc.conversion_rate * 240, "Base margin rate correct");
|
|
||||||
assert.ok(cur_frm.doc.total == 240, "Amount correct");
|
|
||||||
|
|
||||||
},
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,56 +0,0 @@
|
|||||||
QUnit.module('Sales Invoice');
|
|
||||||
|
|
||||||
QUnit.test("test sales Invoice with payment", function(assert) {
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Sales Invoice', [
|
|
||||||
{customer: 'Test Customer 1'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'qty': 5},
|
|
||||||
{'item_code': 'Test Product 1'},
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{update_stock:1},
|
|
||||||
{customer_address: 'Test1-Billing'},
|
|
||||||
{shipping_address_name: 'Test1-Shipping'},
|
|
||||||
{contact_person: 'Contact 1-Test Customer 1'},
|
|
||||||
{taxes_and_charges: 'TEST In State GST - FT'},
|
|
||||||
{tc_name: 'Test Term 1'},
|
|
||||||
{terms: 'This is Test'},
|
|
||||||
{payment_terms_template: '_Test Payment Term Template UI'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => {
|
|
||||||
// get_item_details
|
|
||||||
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
|
|
||||||
// get tax details
|
|
||||||
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
|
|
||||||
// grand_total Calculated
|
|
||||||
assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
|
|
||||||
|
|
||||||
},
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => frappe.tests.click_button('Close'),
|
|
||||||
() => frappe.tests.click_button('Make'),
|
|
||||||
() => frappe.tests.click_link('Payment'),
|
|
||||||
() => frappe.timeout(0.2),
|
|
||||||
() => { cur_frm.set_value('mode_of_payment','Cash');},
|
|
||||||
() => { cur_frm.set_value('paid_to','Cash - '+frappe.get_abbr(frappe.defaults.get_default('Company')));},
|
|
||||||
() => {cur_frm.set_value('reference_no','TEST1234');},
|
|
||||||
() => {cur_frm.set_value('reference_date',frappe.datetime.add_days(frappe.datetime.nowdate(), 0));},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => {
|
|
||||||
// get payment details
|
|
||||||
assert.ok(cur_frm.doc.paid_amount==590, "Paid Amount Correct");
|
|
||||||
},
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,51 +0,0 @@
|
|||||||
QUnit.module('Sales Invoice');
|
|
||||||
|
|
||||||
QUnit.test("test sales Invoice with payment request", function(assert) {
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Sales Invoice', [
|
|
||||||
{customer: 'Test Customer 1'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'qty': 5},
|
|
||||||
{'item_code': 'Test Product 1'},
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{update_stock:1},
|
|
||||||
{customer_address: 'Test1-Billing'},
|
|
||||||
{shipping_address_name: 'Test1-Shipping'},
|
|
||||||
{contact_person: 'Contact 1-Test Customer 1'},
|
|
||||||
{taxes_and_charges: 'TEST In State GST - FT'},
|
|
||||||
{tc_name: 'Test Term 1'},
|
|
||||||
{terms: 'This is Test'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => {
|
|
||||||
// get_item_details
|
|
||||||
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
|
|
||||||
// get tax details
|
|
||||||
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
|
|
||||||
// grand_total Calculated
|
|
||||||
assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
|
|
||||||
|
|
||||||
},
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => frappe.tests.click_button('Close'),
|
|
||||||
() => frappe.tests.click_button('Make'),
|
|
||||||
() => frappe.tests.click_link('Payment Request'),
|
|
||||||
() => frappe.timeout(0.2),
|
|
||||||
() => { cur_frm.set_value('print_format','GST Tax Invoice');},
|
|
||||||
() => { cur_frm.set_value('email_to','test@gmail.com');},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => {
|
|
||||||
// get payment details
|
|
||||||
assert.ok(cur_frm.doc.grand_total==590, "grand total Correct");
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,44 +0,0 @@
|
|||||||
QUnit.module('Sales Invoice');
|
|
||||||
|
|
||||||
QUnit.test("test sales Invoice with serialize item", function(assert) {
|
|
||||||
assert.expect(5);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Sales Invoice', [
|
|
||||||
{customer: 'Test Customer 1'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{'qty': 2},
|
|
||||||
{'item_code': 'Test Product 4'},
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{update_stock:1},
|
|
||||||
{customer_address: 'Test1-Billing'},
|
|
||||||
{shipping_address_name: 'Test1-Shipping'},
|
|
||||||
{contact_person: 'Contact 1-Test Customer 1'},
|
|
||||||
{taxes_and_charges: 'TEST In State GST - FT'},
|
|
||||||
{tc_name: 'Test Term 1'},
|
|
||||||
{terms: 'This is Test'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => {
|
|
||||||
// get_item_details
|
|
||||||
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
|
|
||||||
// get tax details
|
|
||||||
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
|
|
||||||
// get tax account head details
|
|
||||||
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
|
|
||||||
// get batch number
|
|
||||||
assert.ok(cur_frm.doc.items[0].batch_no=='TEST-BATCH-001', " Batch Details correct");
|
|
||||||
// grand_total Calculated
|
|
||||||
assert.ok(cur_frm.doc.grand_total==218, "Grad Total correct");
|
|
||||||
|
|
||||||
},
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,28 +0,0 @@
|
|||||||
QUnit.module('Sales Taxes and Charges Template');
|
|
||||||
|
|
||||||
QUnit.test("test sales taxes and charges template", function(assert) {
|
|
||||||
assert.expect(2);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Sales Taxes and Charges Template', [
|
|
||||||
{title: "TEST In State GST"},
|
|
||||||
{taxes:[
|
|
||||||
[
|
|
||||||
{charge_type:"On Net Total"},
|
|
||||||
{account_head:"CGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{charge_type:"On Net Total"},
|
|
||||||
{account_head:"SGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.title=='TEST In State GST');
|
|
||||||
assert.ok(cur_frm.doc.name=='TEST In State GST - FT');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,36 +0,0 @@
|
|||||||
QUnit.module('Shipping Rule');
|
|
||||||
|
|
||||||
QUnit.test("test Shipping Rule", function(assert) {
|
|
||||||
assert.expect(1);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make("Shipping Rule", [
|
|
||||||
{label: "Next Day Shipping"},
|
|
||||||
{shipping_rule_type: "Selling"},
|
|
||||||
{calculate_based_on: 'Net Total'},
|
|
||||||
{conditions:[
|
|
||||||
[
|
|
||||||
{from_value:1},
|
|
||||||
{to_value:200},
|
|
||||||
{shipping_amount:100}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{from_value:201},
|
|
||||||
{to_value:2000},
|
|
||||||
{shipping_amount:50}
|
|
||||||
],
|
|
||||||
]},
|
|
||||||
{countries:[
|
|
||||||
[
|
|
||||||
{country:'India'}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{account:'Accounts Payable - '+frappe.get_abbr(frappe.defaults.get_default("Company"))},
|
|
||||||
{cost_center:'Main - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {assert.ok(cur_frm.doc.name=='Next Day Shipping');},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,36 +0,0 @@
|
|||||||
QUnit.module('Shipping Rule');
|
|
||||||
|
|
||||||
QUnit.test("test Shipping Rule", function(assert) {
|
|
||||||
assert.expect(1);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make("Shipping Rule", [
|
|
||||||
{label: "Two Day Shipping"},
|
|
||||||
{shipping_rule_type: "Buying"},
|
|
||||||
{fixed_shipping_amount: 0},
|
|
||||||
{conditions:[
|
|
||||||
[
|
|
||||||
{from_value:1},
|
|
||||||
{to_value:200},
|
|
||||||
{shipping_amount:100}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{from_value:201},
|
|
||||||
{to_value:3000},
|
|
||||||
{shipping_amount:200}
|
|
||||||
],
|
|
||||||
]},
|
|
||||||
{countries:[
|
|
||||||
[
|
|
||||||
{country:'India'}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{account:'Accounts Payable - '+frappe.get_abbr(frappe.defaults.get_default("Company"))},
|
|
||||||
{cost_center:'Main - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {assert.ok(cur_frm.doc.name=='Two Day Shipping');},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -23,6 +23,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
|||||||
get_accounting_dimensions,
|
get_accounting_dimensions,
|
||||||
)
|
)
|
||||||
from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
|
from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
|
||||||
|
from erpnext.accounts.party import get_party_account_currency
|
||||||
|
|
||||||
|
|
||||||
class Subscription(Document):
|
class Subscription(Document):
|
||||||
@ -355,6 +356,9 @@ class Subscription(Document):
|
|||||||
if frappe.db.get_value('Supplier', self.party, 'tax_withholding_category'):
|
if frappe.db.get_value('Supplier', self.party, 'tax_withholding_category'):
|
||||||
invoice.apply_tds = 1
|
invoice.apply_tds = 1
|
||||||
|
|
||||||
|
### Add party currency to invoice
|
||||||
|
invoice.currency = get_party_account_currency(self.party_type, self.party, self.company)
|
||||||
|
|
||||||
## Add dimensions in invoice for subscription:
|
## Add dimensions in invoice for subscription:
|
||||||
accounting_dimensions = get_accounting_dimensions()
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// rename this file from _test_[name] to test_[name] to activate
|
|
||||||
// and remove above this line
|
|
||||||
|
|
||||||
QUnit.test("test: Subscription", function (assert) {
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
// insert a new Subscription
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make("Subscription", [
|
|
||||||
{reference_doctype: 'Sales Invoice'},
|
|
||||||
{reference_document: 'SINV-00004'},
|
|
||||||
{start_date: frappe.datetime.month_start()},
|
|
||||||
{end_date: frappe.datetime.month_end()},
|
|
||||||
{frequency: 'Weekly'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.savesubmit(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Yes'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.frequency.includes("Weekly"), "Set frequency Weekly");
|
|
||||||
assert.ok(cur_frm.doc.reference_doctype.includes("Sales Invoice"), "Set base doctype Sales Invoice");
|
|
||||||
assert.equal(cur_frm.doc.docstatus, 1, "Submitted subscription");
|
|
||||||
assert.equal(cur_frm.doc.next_schedule_date,
|
|
||||||
frappe.datetime.add_days(frappe.datetime.get_today(), 7), "Set schedule date");
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -60,15 +60,38 @@ def create_plan():
|
|||||||
plan.billing_interval_count = 3
|
plan.billing_interval_count = 3
|
||||||
plan.insert()
|
plan.insert()
|
||||||
|
|
||||||
|
if not frappe.db.exists('Subscription Plan', '_Test Plan Multicurrency'):
|
||||||
|
plan = frappe.new_doc('Subscription Plan')
|
||||||
|
plan.plan_name = '_Test Plan Multicurrency'
|
||||||
|
plan.item = '_Test Non Stock Item'
|
||||||
|
plan.price_determination = "Fixed Rate"
|
||||||
|
plan.cost = 50
|
||||||
|
plan.currency = 'USD'
|
||||||
|
plan.billing_interval = 'Month'
|
||||||
|
plan.billing_interval_count = 1
|
||||||
|
plan.insert()
|
||||||
|
|
||||||
|
def create_parties():
|
||||||
if not frappe.db.exists('Supplier', '_Test Supplier'):
|
if not frappe.db.exists('Supplier', '_Test Supplier'):
|
||||||
supplier = frappe.new_doc('Supplier')
|
supplier = frappe.new_doc('Supplier')
|
||||||
supplier.supplier_name = '_Test Supplier'
|
supplier.supplier_name = '_Test Supplier'
|
||||||
supplier.supplier_group = 'All Supplier Groups'
|
supplier.supplier_group = 'All Supplier Groups'
|
||||||
supplier.insert()
|
supplier.insert()
|
||||||
|
|
||||||
|
if not frappe.db.exists('Customer', '_Test Subscription Customer'):
|
||||||
|
customer = frappe.new_doc('Customer')
|
||||||
|
customer.customer_name = '_Test Subscription Customer'
|
||||||
|
customer.billing_currency = 'USD'
|
||||||
|
customer.append('accounts', {
|
||||||
|
'company': '_Test Company',
|
||||||
|
'account': '_Test Receivable USD - _TC'
|
||||||
|
})
|
||||||
|
customer.insert()
|
||||||
|
|
||||||
class TestSubscription(unittest.TestCase):
|
class TestSubscription(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
create_plan()
|
create_plan()
|
||||||
|
create_parties()
|
||||||
|
|
||||||
def test_create_subscription_with_trial_with_correct_period(self):
|
def test_create_subscription_with_trial_with_correct_period(self):
|
||||||
subscription = frappe.new_doc('Subscription')
|
subscription = frappe.new_doc('Subscription')
|
||||||
@ -637,3 +660,22 @@ class TestSubscription(unittest.TestCase):
|
|||||||
|
|
||||||
subscription.process()
|
subscription.process()
|
||||||
self.assertEqual(len(subscription.invoices), 1)
|
self.assertEqual(len(subscription.invoices), 1)
|
||||||
|
|
||||||
|
def test_multicurrency_subscription(self):
|
||||||
|
subscription = frappe.new_doc('Subscription')
|
||||||
|
subscription.party_type = 'Customer'
|
||||||
|
subscription.party = '_Test Subscription Customer'
|
||||||
|
subscription.generate_invoice_at_period_start = 1
|
||||||
|
subscription.company = '_Test Company'
|
||||||
|
# select subscription start date as '2018-01-15'
|
||||||
|
subscription.start_date = '2018-01-01'
|
||||||
|
subscription.append('plans', {'plan': '_Test Plan Multicurrency', 'qty': 1})
|
||||||
|
subscription.save()
|
||||||
|
|
||||||
|
subscription.process()
|
||||||
|
self.assertEqual(len(subscription.invoices), 1)
|
||||||
|
self.assertEqual(subscription.status, 'Unpaid')
|
||||||
|
|
||||||
|
# Check the currency of the created invoice
|
||||||
|
currency = frappe.db.get_value('Sales Invoice', subscription.invoices[0].invoice, 'currency')
|
||||||
|
self.assertEqual(currency, 'USD')
|
@ -75,7 +75,8 @@
|
|||||||
"fieldname": "cost",
|
"fieldname": "cost",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Cost"
|
"label": "Cost",
|
||||||
|
"options": "currency"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.price_determination==\"Based On Price List\"",
|
"depends_on": "eval:doc.price_determination==\"Based On Price List\"",
|
||||||
@ -147,7 +148,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-08-13 10:53:44.205774",
|
"modified": "2021-12-10 15:24:15.794477",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Subscription Plan",
|
"name": "Subscription Plan",
|
||||||
|
@ -545,7 +545,9 @@ class ReceivablePayableReport(object):
|
|||||||
|
|
||||||
def set_ageing(self, row):
|
def set_ageing(self, row):
|
||||||
if self.filters.ageing_based_on == "Due Date":
|
if self.filters.ageing_based_on == "Due Date":
|
||||||
entry_date = row.due_date
|
# use posting date as a fallback for advances posted via journal and payment entry
|
||||||
|
# when ageing viewed by due date
|
||||||
|
entry_date = row.due_date or row.posting_date
|
||||||
elif self.filters.ageing_based_on == "Supplier Invoice Date":
|
elif self.filters.ageing_based_on == "Supplier Invoice Date":
|
||||||
entry_date = row.bill_date
|
entry_date = row.bill_date
|
||||||
else:
|
else:
|
||||||
|
@ -370,7 +370,7 @@ def get_account_heads(root_type, companies, filters):
|
|||||||
accounts = get_accounts(root_type, filters)
|
accounts = get_accounts(root_type, filters)
|
||||||
|
|
||||||
if not accounts:
|
if not accounts:
|
||||||
return None, None
|
return None, None, None
|
||||||
|
|
||||||
accounts = update_parent_account_names(accounts)
|
accounts = update_parent_account_names(accounts)
|
||||||
|
|
||||||
|
@ -0,0 +1,114 @@
|
|||||||
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
function get_filters() {
|
||||||
|
let filters = [
|
||||||
|
{
|
||||||
|
"fieldname":"company",
|
||||||
|
"label": __("Company"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Company",
|
||||||
|
"default": frappe.defaults.get_user_default("Company"),
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"filter_based_on",
|
||||||
|
"label": __("Filter Based On"),
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"options": ["Fiscal Year", "Date Range"],
|
||||||
|
"default": ["Fiscal Year"],
|
||||||
|
"reqd": 1,
|
||||||
|
on_change: function() {
|
||||||
|
let filter_based_on = frappe.query_report.get_filter_value('filter_based_on');
|
||||||
|
frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range');
|
||||||
|
frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range');
|
||||||
|
frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year');
|
||||||
|
frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year');
|
||||||
|
|
||||||
|
frappe.query_report.refresh();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"period_start_date",
|
||||||
|
"label": __("Start Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"hidden": 1,
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"period_end_date",
|
||||||
|
"label": __("End Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"hidden": 1,
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"from_fiscal_year",
|
||||||
|
"label": __("Start Year"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Fiscal Year",
|
||||||
|
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"to_fiscal_year",
|
||||||
|
"label": __("End Year"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Fiscal Year",
|
||||||
|
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "periodicity",
|
||||||
|
"label": __("Periodicity"),
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"options": [
|
||||||
|
{ "value": "Monthly", "label": __("Monthly") },
|
||||||
|
{ "value": "Quarterly", "label": __("Quarterly") },
|
||||||
|
{ "value": "Half-Yearly", "label": __("Half-Yearly") },
|
||||||
|
{ "value": "Yearly", "label": __("Yearly") }
|
||||||
|
],
|
||||||
|
"default": "Monthly",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "type",
|
||||||
|
"label": __("Invoice Type"),
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"options": [
|
||||||
|
{ "value": "Revenue", "label": __("Revenue") },
|
||||||
|
{ "value": "Expense", "label": __("Expense") }
|
||||||
|
],
|
||||||
|
"default": "Revenue",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname" : "with_upcoming_postings",
|
||||||
|
"label": __("Show with upcoming revenue/expense"),
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"default": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.query_reports["Deferred Revenue and Expense"] = {
|
||||||
|
"filters": get_filters(),
|
||||||
|
"formatter": function(value, row, column, data, default_formatter){
|
||||||
|
return default_formatter(value, row, column, data);
|
||||||
|
},
|
||||||
|
onload: function(report){
|
||||||
|
let fiscal_year = frappe.defaults.get_user_default("fiscal_year");
|
||||||
|
|
||||||
|
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
|
||||||
|
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
|
||||||
|
frappe.query_report.set_filter_value({
|
||||||
|
period_start_date: fy.year_start_date,
|
||||||
|
period_end_date: fy.year_end_date
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
|
"columns": [],
|
||||||
|
"creation": "2021-12-10 19:27:14.654220",
|
||||||
|
"disable_prepared_report": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"filters": [],
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"modified": "2021-12-10 19:27:14.654220",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Deferred Revenue and Expense",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "GL Entry",
|
||||||
|
"report_name": "Deferred Revenue and Expense",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "Accounts User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Accounts Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Auditor"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,440 @@
|
|||||||
|
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# License: MIT. See LICENSE
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import _, qb
|
||||||
|
from frappe.query_builder import Column, functions
|
||||||
|
from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, rounded
|
||||||
|
|
||||||
|
from erpnext.accounts.report.financial_statements import get_period_list
|
||||||
|
|
||||||
|
|
||||||
|
class Deferred_Item(object):
|
||||||
|
"""
|
||||||
|
Helper class for processing items with deferred revenue/expense
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, item, inv, gle_entries):
|
||||||
|
self.name = item
|
||||||
|
self.parent = inv.name
|
||||||
|
self.item_name = gle_entries[0].item_name
|
||||||
|
self.service_start_date = gle_entries[0].service_start_date
|
||||||
|
self.service_end_date = gle_entries[0].service_end_date
|
||||||
|
self.base_net_amount = gle_entries[0].base_net_amount
|
||||||
|
self.filters = inv.filters
|
||||||
|
self.period_list = inv.period_list
|
||||||
|
|
||||||
|
if gle_entries[0].deferred_revenue_account:
|
||||||
|
self.type = "Deferred Sale Item"
|
||||||
|
self.deferred_account = gle_entries[0].deferred_revenue_account
|
||||||
|
elif gle_entries[0].deferred_expense_account:
|
||||||
|
self.type = "Deferred Purchase Item"
|
||||||
|
self.deferred_account = gle_entries[0].deferred_expense_account
|
||||||
|
|
||||||
|
self.gle_entries = []
|
||||||
|
# holds period wise total for item
|
||||||
|
self.period_total = []
|
||||||
|
self.last_entry_date = self.service_start_date
|
||||||
|
|
||||||
|
if gle_entries:
|
||||||
|
self.gle_entries = gle_entries
|
||||||
|
for x in self.gle_entries:
|
||||||
|
if self.get_amount(x):
|
||||||
|
self.last_entry_date = x.gle_posting_date
|
||||||
|
|
||||||
|
def report_data(self):
|
||||||
|
"""
|
||||||
|
Generate report data for output
|
||||||
|
"""
|
||||||
|
ret_data = frappe._dict({"name": self.item_name})
|
||||||
|
for period in self.period_total:
|
||||||
|
ret_data[period.key] = period.total
|
||||||
|
ret_data.indent = 1
|
||||||
|
return ret_data
|
||||||
|
|
||||||
|
def get_amount(self, entry):
|
||||||
|
"""
|
||||||
|
For a given GL/Journal posting, get balance based on item type
|
||||||
|
"""
|
||||||
|
if self.type == "Deferred Sale Item":
|
||||||
|
return entry.debit - entry.credit
|
||||||
|
elif self.type == "Deferred Purchase Item":
|
||||||
|
return -(entry.credit - entry.debit)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def get_item_total(self):
|
||||||
|
"""
|
||||||
|
Helper method - calculate booked amount. Includes simulated postings as well
|
||||||
|
"""
|
||||||
|
total = 0
|
||||||
|
for gle_posting in self.gle_entries:
|
||||||
|
total += self.get_amount(gle_posting)
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
def calculate_amount(self, start_date, end_date):
|
||||||
|
"""
|
||||||
|
start_date, end_date - datetime.datetime.date
|
||||||
|
return - estimated amount to post for given period
|
||||||
|
Calculated based on already booked amount and item service period
|
||||||
|
"""
|
||||||
|
total_months = (
|
||||||
|
(self.service_end_date.year - self.service_start_date.year) * 12
|
||||||
|
+ (self.service_end_date.month - self.service_start_date.month)
|
||||||
|
+ 1
|
||||||
|
)
|
||||||
|
|
||||||
|
prorate = date_diff(self.service_end_date, self.service_start_date) / date_diff(
|
||||||
|
get_last_day(self.service_end_date), get_first_day(self.service_start_date)
|
||||||
|
)
|
||||||
|
|
||||||
|
actual_months = rounded(total_months * prorate, 1)
|
||||||
|
|
||||||
|
already_booked_amount = self.get_item_total()
|
||||||
|
base_amount = self.base_net_amount / actual_months
|
||||||
|
|
||||||
|
if base_amount + already_booked_amount > self.base_net_amount:
|
||||||
|
base_amount = self.base_net_amount - already_booked_amount
|
||||||
|
|
||||||
|
if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date):
|
||||||
|
partial_month = flt(date_diff(end_date, start_date)) / flt(
|
||||||
|
date_diff(get_last_day(end_date), get_first_day(start_date))
|
||||||
|
)
|
||||||
|
base_amount *= rounded(partial_month, 1)
|
||||||
|
|
||||||
|
return base_amount
|
||||||
|
|
||||||
|
def make_dummy_gle(self, name, date, amount):
|
||||||
|
"""
|
||||||
|
return - frappe._dict() of a dummy gle entry
|
||||||
|
"""
|
||||||
|
entry = frappe._dict(
|
||||||
|
{"name": name, "gle_posting_date": date, "debit": 0, "credit": 0, "posted": "not"}
|
||||||
|
)
|
||||||
|
if self.type == "Deferred Sale Item":
|
||||||
|
entry.debit = amount
|
||||||
|
elif self.type == "Deferred Purchase Item":
|
||||||
|
entry.credit = amount
|
||||||
|
return entry
|
||||||
|
|
||||||
|
def simulate_future_posting(self):
|
||||||
|
"""
|
||||||
|
simulate future posting by creating dummy gl entries. starts from the last posting date.
|
||||||
|
"""
|
||||||
|
if add_days(self.last_entry_date, 1) < self.period_list[-1].to_date:
|
||||||
|
self.estimate_for_period_list = get_period_list(
|
||||||
|
self.filters.from_fiscal_year,
|
||||||
|
self.filters.to_fiscal_year,
|
||||||
|
add_days(self.last_entry_date, 1),
|
||||||
|
self.period_list[-1].to_date,
|
||||||
|
"Date Range",
|
||||||
|
"Monthly",
|
||||||
|
company=self.filters.company,
|
||||||
|
)
|
||||||
|
for period in self.estimate_for_period_list:
|
||||||
|
amount = self.calculate_amount(period.from_date, period.to_date)
|
||||||
|
gle = self.make_dummy_gle(period.key, period.to_date, amount)
|
||||||
|
self.gle_entries.append(gle)
|
||||||
|
|
||||||
|
def calculate_item_revenue_expense_for_period(self):
|
||||||
|
"""
|
||||||
|
calculate item postings for each period and update period_total list
|
||||||
|
"""
|
||||||
|
for period in self.period_list:
|
||||||
|
period_sum = 0
|
||||||
|
actual = 0
|
||||||
|
for posting in self.gle_entries:
|
||||||
|
# if period.from_date <= posting.posting_date <= period.to_date:
|
||||||
|
if period.from_date <= posting.gle_posting_date <= period.to_date:
|
||||||
|
period_sum += self.get_amount(posting)
|
||||||
|
if posting.posted == "posted":
|
||||||
|
actual += self.get_amount(posting)
|
||||||
|
|
||||||
|
self.period_total.append(
|
||||||
|
frappe._dict({"key": period.key, "total": period_sum, "actual": actual})
|
||||||
|
)
|
||||||
|
return self.period_total
|
||||||
|
|
||||||
|
|
||||||
|
class Deferred_Invoice(object):
|
||||||
|
def __init__(self, invoice, items, filters, period_list):
|
||||||
|
"""
|
||||||
|
Helper class for processing invoices with deferred revenue/expense items
|
||||||
|
invoice - string : invoice name
|
||||||
|
items - list : frappe._dict() with item details. Refer Deferred_Item for required fields
|
||||||
|
"""
|
||||||
|
self.name = invoice
|
||||||
|
self.posting_date = items[0].posting_date
|
||||||
|
self.filters = filters
|
||||||
|
self.period_list = period_list
|
||||||
|
# holds period wise total for invoice
|
||||||
|
self.period_total = []
|
||||||
|
|
||||||
|
if items[0].deferred_revenue_account:
|
||||||
|
self.type = "Sales"
|
||||||
|
elif items[0].deferred_expense_account:
|
||||||
|
self.type = "Purchase"
|
||||||
|
|
||||||
|
self.items = []
|
||||||
|
# for each uniq items
|
||||||
|
self.uniq_items = set([x.item for x in items])
|
||||||
|
for item in self.uniq_items:
|
||||||
|
self.items.append(Deferred_Item(item, self, [x for x in items if x.item == item]))
|
||||||
|
|
||||||
|
def calculate_invoice_revenue_expense_for_period(self):
|
||||||
|
"""
|
||||||
|
calculate deferred revenue/expense for all items in invoice
|
||||||
|
"""
|
||||||
|
# initialize period_total list for invoice
|
||||||
|
for period in self.period_list:
|
||||||
|
self.period_total.append(frappe._dict({"key": period.key, "total": 0, "actual": 0}))
|
||||||
|
|
||||||
|
for item in self.items:
|
||||||
|
item_total = item.calculate_item_revenue_expense_for_period()
|
||||||
|
# update invoice total
|
||||||
|
for idx, period in enumerate(self.period_list, 0):
|
||||||
|
self.period_total[idx].total += item_total[idx].total
|
||||||
|
self.period_total[idx].actual += item_total[idx].actual
|
||||||
|
return self.period_total
|
||||||
|
|
||||||
|
def estimate_future(self):
|
||||||
|
"""
|
||||||
|
create dummy GL entries for upcoming months for all items in invoice
|
||||||
|
"""
|
||||||
|
[item.simulate_future_posting() for item in self.items]
|
||||||
|
|
||||||
|
def report_data(self):
|
||||||
|
"""
|
||||||
|
generate report data for invoice, includes invoice total
|
||||||
|
"""
|
||||||
|
ret_data = []
|
||||||
|
inv_total = frappe._dict({"name": self.name})
|
||||||
|
for x in self.period_total:
|
||||||
|
inv_total[x.key] = x.total
|
||||||
|
inv_total.indent = 0
|
||||||
|
ret_data.append(inv_total)
|
||||||
|
list(map(lambda item: ret_data.append(item.report_data()), self.items))
|
||||||
|
return ret_data
|
||||||
|
|
||||||
|
|
||||||
|
class Deferred_Revenue_and_Expense_Report(object):
|
||||||
|
def __init__(self, filters=None):
|
||||||
|
"""
|
||||||
|
Initialize deferred revenue/expense report with user provided filters or system defaults, if none is provided
|
||||||
|
"""
|
||||||
|
|
||||||
|
# If no filters are provided, get user defaults
|
||||||
|
if not filters:
|
||||||
|
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
|
||||||
|
self.filters = frappe._dict(
|
||||||
|
{
|
||||||
|
"company": frappe.defaults.get_user_default("Company"),
|
||||||
|
"filter_based_on": "Fiscal Year",
|
||||||
|
"period_start_date": fiscal_year.year_start_date,
|
||||||
|
"period_end_date": fiscal_year.year_end_date,
|
||||||
|
"from_fiscal_year": fiscal_year.year,
|
||||||
|
"to_fiscal_year": fiscal_year.year,
|
||||||
|
"periodicity": "Monthly",
|
||||||
|
"type": "Revenue",
|
||||||
|
"with_upcoming_postings": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.filters = frappe._dict(filters)
|
||||||
|
|
||||||
|
self.period_list = None
|
||||||
|
self.deferred_invoices = []
|
||||||
|
# holds period wise total for report
|
||||||
|
self.period_total = []
|
||||||
|
|
||||||
|
def get_period_list(self):
|
||||||
|
"""
|
||||||
|
Figure out selected period based on filters
|
||||||
|
"""
|
||||||
|
self.period_list = get_period_list(
|
||||||
|
self.filters.from_fiscal_year,
|
||||||
|
self.filters.to_fiscal_year,
|
||||||
|
self.filters.period_start_date,
|
||||||
|
self.filters.period_end_date,
|
||||||
|
self.filters.filter_based_on,
|
||||||
|
self.filters.periodicity,
|
||||||
|
company=self.filters.company,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_invoices(self):
|
||||||
|
"""
|
||||||
|
Get all sales and purchase invoices which has deferred revenue/expense items
|
||||||
|
"""
|
||||||
|
gle = qb.DocType("GL Entry")
|
||||||
|
# column doesn't have an alias option
|
||||||
|
posted = Column("posted")
|
||||||
|
|
||||||
|
if self.filters.type == "Revenue":
|
||||||
|
inv = qb.DocType("Sales Invoice")
|
||||||
|
inv_item = qb.DocType("Sales Invoice Item")
|
||||||
|
deferred_flag_field = inv_item["enable_deferred_revenue"]
|
||||||
|
deferred_account_field = inv_item["deferred_revenue_account"]
|
||||||
|
|
||||||
|
elif self.filters.type == "Expense":
|
||||||
|
inv = qb.DocType("Purchase Invoice")
|
||||||
|
inv_item = qb.DocType("Purchase Invoice Item")
|
||||||
|
deferred_flag_field = inv_item["enable_deferred_expense"]
|
||||||
|
deferred_account_field = inv_item["deferred_expense_account"]
|
||||||
|
|
||||||
|
query = (
|
||||||
|
qb.from_(inv_item)
|
||||||
|
.join(inv)
|
||||||
|
.on(inv.name == inv_item.parent)
|
||||||
|
.join(gle)
|
||||||
|
.on((inv_item.name == gle.voucher_detail_no) & (deferred_account_field == gle.account))
|
||||||
|
.select(
|
||||||
|
inv.name.as_("doc"),
|
||||||
|
inv.posting_date,
|
||||||
|
inv_item.name.as_("item"),
|
||||||
|
inv_item.item_name,
|
||||||
|
inv_item.service_start_date,
|
||||||
|
inv_item.service_end_date,
|
||||||
|
inv_item.base_net_amount,
|
||||||
|
deferred_account_field,
|
||||||
|
gle.posting_date.as_("gle_posting_date"),
|
||||||
|
functions.Sum(gle.debit).as_("debit"),
|
||||||
|
functions.Sum(gle.credit).as_("credit"),
|
||||||
|
posted,
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(inv.docstatus == 1)
|
||||||
|
& (deferred_flag_field == 1)
|
||||||
|
& (
|
||||||
|
(
|
||||||
|
(self.period_list[0].from_date >= inv_item.service_start_date)
|
||||||
|
& (inv_item.service_end_date >= self.period_list[0].from_date)
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
(inv_item.service_start_date >= self.period_list[0].from_date)
|
||||||
|
& (inv_item.service_start_date <= self.period_list[-1].to_date)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.groupby(inv.name, inv_item.name, gle.posting_date)
|
||||||
|
.orderby(gle.posting_date)
|
||||||
|
)
|
||||||
|
self.invoices = query.run(as_dict=True)
|
||||||
|
|
||||||
|
uniq_invoice = set([x.doc for x in self.invoices])
|
||||||
|
for inv in uniq_invoice:
|
||||||
|
self.deferred_invoices.append(
|
||||||
|
Deferred_Invoice(
|
||||||
|
inv, [x for x in self.invoices if x.doc == inv], self.filters, self.period_list
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def estimate_future(self):
|
||||||
|
"""
|
||||||
|
For all Invoices estimate upcoming postings
|
||||||
|
"""
|
||||||
|
for x in self.deferred_invoices:
|
||||||
|
x.estimate_future()
|
||||||
|
|
||||||
|
def calculate_revenue_and_expense(self):
|
||||||
|
"""
|
||||||
|
calculate the deferred revenue/expense for all invoices
|
||||||
|
"""
|
||||||
|
# initialize period_total list for report
|
||||||
|
for period in self.period_list:
|
||||||
|
self.period_total.append(frappe._dict({"key": period.key, "total": 0, "actual": 0}))
|
||||||
|
|
||||||
|
for inv in self.deferred_invoices:
|
||||||
|
inv_total = inv.calculate_invoice_revenue_expense_for_period()
|
||||||
|
# calculate total for whole report
|
||||||
|
for idx, period in enumerate(self.period_list, 0):
|
||||||
|
self.period_total[idx].total += inv_total[idx].total
|
||||||
|
self.period_total[idx].actual += inv_total[idx].actual
|
||||||
|
|
||||||
|
def get_columns(self):
|
||||||
|
columns = []
|
||||||
|
columns.append({"label": _("Name"), "fieldname": "name", "fieldtype": "Data", "read_only": 1})
|
||||||
|
for period in self.period_list:
|
||||||
|
columns.append(
|
||||||
|
{
|
||||||
|
"label": _(period.label),
|
||||||
|
"fieldname": period.key,
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"read_only": 1,
|
||||||
|
})
|
||||||
|
return columns
|
||||||
|
|
||||||
|
def generate_report_data(self):
|
||||||
|
"""
|
||||||
|
Generate report data for all invoices. Adds total rows for revenue and expense
|
||||||
|
"""
|
||||||
|
ret = []
|
||||||
|
|
||||||
|
for inv in self.deferred_invoices:
|
||||||
|
ret += inv.report_data()
|
||||||
|
|
||||||
|
# empty row for padding
|
||||||
|
ret += [{}]
|
||||||
|
|
||||||
|
# add total row
|
||||||
|
if ret is not []:
|
||||||
|
if self.filters.type == "Revenue":
|
||||||
|
total_row = frappe._dict({"name": "Total Deferred Income"})
|
||||||
|
elif self.filters.type == "Expense":
|
||||||
|
total_row = frappe._dict({"name": "Total Deferred Expense"})
|
||||||
|
|
||||||
|
for idx, period in enumerate(self.period_list, 0):
|
||||||
|
total_row[period.key] = self.period_total[idx].total
|
||||||
|
ret.append(total_row)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def prepare_chart(self):
|
||||||
|
chart = {
|
||||||
|
"data": {
|
||||||
|
"labels": [period.label for period in self.period_list],
|
||||||
|
"datasets": [
|
||||||
|
{
|
||||||
|
"name": "Actual Posting",
|
||||||
|
"chartType": "bar",
|
||||||
|
"values": [x.actual for x in self.period_total],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"type": "axis-mixed",
|
||||||
|
"height": 500,
|
||||||
|
"axisOptions": {"xAxisMode": "Tick", "xIsSeries": True},
|
||||||
|
"barOptions": {"stacked": False, "spaceRatio": 0.5},
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.filters.with_upcoming_postings:
|
||||||
|
chart["data"]["datasets"].append({
|
||||||
|
"name": "Expected",
|
||||||
|
"chartType": "line",
|
||||||
|
"values": [x.total for x in self.period_total]
|
||||||
|
})
|
||||||
|
|
||||||
|
return chart
|
||||||
|
|
||||||
|
def run(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Run report and generate data
|
||||||
|
"""
|
||||||
|
self.deferred_invoices.clear()
|
||||||
|
self.get_period_list()
|
||||||
|
self.get_invoices()
|
||||||
|
|
||||||
|
if self.filters.with_upcoming_postings:
|
||||||
|
self.estimate_future()
|
||||||
|
self.calculate_revenue_and_expense()
|
||||||
|
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
report = Deferred_Revenue_and_Expense_Report(filters=filters)
|
||||||
|
report.run()
|
||||||
|
|
||||||
|
columns = report.get_columns()
|
||||||
|
data = report.generate_report_data()
|
||||||
|
message = []
|
||||||
|
chart = report.prepare_chart()
|
||||||
|
|
||||||
|
return columns, data, message, chart
|
@ -0,0 +1,253 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import qb
|
||||||
|
from frappe.utils import nowdate
|
||||||
|
|
||||||
|
from erpnext.accounts.doctype.account.test_account import create_account
|
||||||
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
from erpnext.accounts.report.deferred_revenue_and_expense.deferred_revenue_and_expense import (
|
||||||
|
Deferred_Revenue_and_Expense_Report,
|
||||||
|
)
|
||||||
|
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
||||||
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
|
|
||||||
|
|
||||||
|
class TestDeferredRevenueAndExpense(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(self):
|
||||||
|
clear_old_entries()
|
||||||
|
create_company()
|
||||||
|
|
||||||
|
def test_deferred_revenue(self):
|
||||||
|
# created deferred expense accounts, if not found
|
||||||
|
deferred_revenue_account = create_account(
|
||||||
|
account_name="Deferred Revenue",
|
||||||
|
parent_account="Current Liabilities - _CD",
|
||||||
|
company="_Test Company DR",
|
||||||
|
)
|
||||||
|
|
||||||
|
acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
|
||||||
|
acc_settings.book_deferred_entries_based_on = "Months"
|
||||||
|
acc_settings.save()
|
||||||
|
|
||||||
|
customer = frappe.new_doc("Customer")
|
||||||
|
customer.customer_name = "_Test Customer DR"
|
||||||
|
customer.type = "Individual"
|
||||||
|
customer.insert()
|
||||||
|
|
||||||
|
item = create_item(
|
||||||
|
"_Test Internet Subscription",
|
||||||
|
is_stock_item=0,
|
||||||
|
warehouse="All Warehouses - _CD",
|
||||||
|
company="_Test Company DR",
|
||||||
|
)
|
||||||
|
item.enable_deferred_revenue = 1
|
||||||
|
item.deferred_revenue_account = deferred_revenue_account
|
||||||
|
item.no_of_months = 3
|
||||||
|
item.save()
|
||||||
|
|
||||||
|
si = create_sales_invoice(
|
||||||
|
item=item.name,
|
||||||
|
company="_Test Company DR",
|
||||||
|
customer="_Test Customer DR",
|
||||||
|
debit_to="Debtors - _CD",
|
||||||
|
posting_date="2021-05-01",
|
||||||
|
parent_cost_center="Main - _CD",
|
||||||
|
cost_center="Main - _CD",
|
||||||
|
do_not_submit=True,
|
||||||
|
rate=300,
|
||||||
|
price_list_rate=300,
|
||||||
|
)
|
||||||
|
si.items[0].enable_deferred_revenue = 1
|
||||||
|
si.items[0].service_start_date = "2021-05-01"
|
||||||
|
si.items[0].service_end_date = "2021-08-01"
|
||||||
|
si.items[0].deferred_revenue_account = deferred_revenue_account
|
||||||
|
si.items[0].income_account = "Sales - _CD"
|
||||||
|
si.save()
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
pda = frappe.get_doc(
|
||||||
|
dict(
|
||||||
|
doctype="Process Deferred Accounting",
|
||||||
|
posting_date=nowdate(),
|
||||||
|
start_date="2021-05-01",
|
||||||
|
end_date="2021-08-01",
|
||||||
|
type="Income",
|
||||||
|
company="_Test Company DR",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
pda.insert()
|
||||||
|
pda.submit()
|
||||||
|
|
||||||
|
# execute report
|
||||||
|
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
|
||||||
|
self.filters = frappe._dict(
|
||||||
|
{
|
||||||
|
"company": frappe.defaults.get_user_default("Company"),
|
||||||
|
"filter_based_on": "Date Range",
|
||||||
|
"period_start_date": "2021-05-01",
|
||||||
|
"period_end_date": "2021-08-01",
|
||||||
|
"from_fiscal_year": fiscal_year.year,
|
||||||
|
"to_fiscal_year": fiscal_year.year,
|
||||||
|
"periodicity": "Monthly",
|
||||||
|
"type": "Revenue",
|
||||||
|
"with_upcoming_postings": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
report = Deferred_Revenue_and_Expense_Report(filters=self.filters)
|
||||||
|
report.run()
|
||||||
|
expected = [
|
||||||
|
{"key": "may_2021", "total": 100.0, "actual": 100.0},
|
||||||
|
{"key": "jun_2021", "total": 100.0, "actual": 100.0},
|
||||||
|
{"key": "jul_2021", "total": 100.0, "actual": 100.0},
|
||||||
|
{"key": "aug_2021", "total": 0, "actual": 0},
|
||||||
|
]
|
||||||
|
self.assertEqual(report.period_total, expected)
|
||||||
|
|
||||||
|
def test_deferred_expense(self):
|
||||||
|
# created deferred expense accounts, if not found
|
||||||
|
deferred_expense_account = create_account(
|
||||||
|
account_name="Deferred Expense",
|
||||||
|
parent_account="Current Assets - _CD",
|
||||||
|
company="_Test Company DR",
|
||||||
|
)
|
||||||
|
|
||||||
|
acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
|
||||||
|
acc_settings.book_deferred_entries_based_on = "Months"
|
||||||
|
acc_settings.save()
|
||||||
|
|
||||||
|
supplier = create_supplier(
|
||||||
|
supplier_name="_Test Furniture Supplier", supplier_group="Local", supplier_type="Company"
|
||||||
|
)
|
||||||
|
supplier.save()
|
||||||
|
|
||||||
|
item = create_item(
|
||||||
|
"_Test Office Desk",
|
||||||
|
is_stock_item=0,
|
||||||
|
warehouse="All Warehouses - _CD",
|
||||||
|
company="_Test Company DR",
|
||||||
|
)
|
||||||
|
item.enable_deferred_expense = 1
|
||||||
|
item.deferred_expense_account = deferred_expense_account
|
||||||
|
item.no_of_months_exp = 3
|
||||||
|
item.save()
|
||||||
|
|
||||||
|
pi = make_purchase_invoice(
|
||||||
|
item=item.name,
|
||||||
|
company="_Test Company DR",
|
||||||
|
supplier="_Test Furniture Supplier",
|
||||||
|
is_return=False,
|
||||||
|
update_stock=False,
|
||||||
|
posting_date=frappe.utils.datetime.date(2021, 5, 1),
|
||||||
|
parent_cost_center="Main - _CD",
|
||||||
|
cost_center="Main - _CD",
|
||||||
|
do_not_save=True,
|
||||||
|
rate=300,
|
||||||
|
price_list_rate=300,
|
||||||
|
warehouse="All Warehouses - _CD",
|
||||||
|
qty=1,
|
||||||
|
)
|
||||||
|
pi.set_posting_time = True
|
||||||
|
pi.items[0].enable_deferred_expense = 1
|
||||||
|
pi.items[0].service_start_date = "2021-05-01"
|
||||||
|
pi.items[0].service_end_date = "2021-08-01"
|
||||||
|
pi.items[0].deferred_expense_account = deferred_expense_account
|
||||||
|
pi.items[0].expense_account = "Office Maintenance Expenses - _CD"
|
||||||
|
pi.save()
|
||||||
|
pi.submit()
|
||||||
|
|
||||||
|
pda = frappe.get_doc(
|
||||||
|
dict(
|
||||||
|
doctype="Process Deferred Accounting",
|
||||||
|
posting_date=nowdate(),
|
||||||
|
start_date="2021-05-01",
|
||||||
|
end_date="2021-08-01",
|
||||||
|
type="Expense",
|
||||||
|
company="_Test Company DR",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
pda.insert()
|
||||||
|
pda.submit()
|
||||||
|
|
||||||
|
# execute report
|
||||||
|
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
|
||||||
|
self.filters = frappe._dict(
|
||||||
|
{
|
||||||
|
"company": frappe.defaults.get_user_default("Company"),
|
||||||
|
"filter_based_on": "Date Range",
|
||||||
|
"period_start_date": "2021-05-01",
|
||||||
|
"period_end_date": "2021-08-01",
|
||||||
|
"from_fiscal_year": fiscal_year.year,
|
||||||
|
"to_fiscal_year": fiscal_year.year,
|
||||||
|
"periodicity": "Monthly",
|
||||||
|
"type": "Expense",
|
||||||
|
"with_upcoming_postings": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
report = Deferred_Revenue_and_Expense_Report(filters=self.filters)
|
||||||
|
report.run()
|
||||||
|
expected = [
|
||||||
|
{"key": "may_2021", "total": -100.0, "actual": -100.0},
|
||||||
|
{"key": "jun_2021", "total": -100.0, "actual": -100.0},
|
||||||
|
{"key": "jul_2021", "total": -100.0, "actual": -100.0},
|
||||||
|
{"key": "aug_2021", "total": 0, "actual": 0},
|
||||||
|
]
|
||||||
|
self.assertEqual(report.period_total, expected)
|
||||||
|
|
||||||
|
|
||||||
|
def create_company():
|
||||||
|
company = frappe.db.exists("Company", "_Test Company DR")
|
||||||
|
if not company:
|
||||||
|
company = frappe.new_doc("Company")
|
||||||
|
company.company_name = "_Test Company DR"
|
||||||
|
company.default_currency = "INR"
|
||||||
|
company.chart_of_accounts = "Standard"
|
||||||
|
company.insert()
|
||||||
|
|
||||||
|
|
||||||
|
def clear_old_entries():
|
||||||
|
item = qb.DocType("Item")
|
||||||
|
account = qb.DocType("Account")
|
||||||
|
customer = qb.DocType("Customer")
|
||||||
|
supplier = qb.DocType("Supplier")
|
||||||
|
sinv = qb.DocType("Sales Invoice")
|
||||||
|
sinv_item = qb.DocType("Sales Invoice Item")
|
||||||
|
pinv = qb.DocType("Purchase Invoice")
|
||||||
|
pinv_item = qb.DocType("Purchase Invoice Item")
|
||||||
|
|
||||||
|
qb.from_(account).delete().where(
|
||||||
|
(account.account_name == "Deferred Revenue")
|
||||||
|
| (account.account_name == "Deferred Expense") & (account.company == "_Test Company DR")
|
||||||
|
).run()
|
||||||
|
qb.from_(item).delete().where(
|
||||||
|
(item.item_code == "_Test Internet Subscription") | (item.item_code == "_Test Office Rent")
|
||||||
|
).run()
|
||||||
|
qb.from_(customer).delete().where(customer.customer_name == "_Test Customer DR").run()
|
||||||
|
qb.from_(supplier).delete().where(supplier.supplier_name == "_Test Furniture Supplier").run()
|
||||||
|
|
||||||
|
# delete existing invoices with deferred items
|
||||||
|
deferred_invoices = (
|
||||||
|
qb.from_(sinv)
|
||||||
|
.join(sinv_item)
|
||||||
|
.on(sinv.name == sinv_item.parent)
|
||||||
|
.select(sinv.name)
|
||||||
|
.where(sinv_item.enable_deferred_revenue == 1)
|
||||||
|
.run()
|
||||||
|
)
|
||||||
|
if deferred_invoices:
|
||||||
|
qb.from_(sinv).delete().where(sinv.name.isin(deferred_invoices)).run()
|
||||||
|
|
||||||
|
deferred_invoices = (
|
||||||
|
qb.from_(pinv)
|
||||||
|
.join(pinv_item)
|
||||||
|
.on(pinv.name == pinv_item.parent)
|
||||||
|
.select(pinv.name)
|
||||||
|
.where(pinv_item.enable_deferred_expense == 1)
|
||||||
|
.run()
|
||||||
|
)
|
||||||
|
if deferred_invoices:
|
||||||
|
qb.from_(pinv).delete().where(pinv.name.isin(deferred_invoices)).run()
|
@ -36,12 +36,16 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map):
|
|||||||
posting_date = entry.posting_date
|
posting_date = entry.posting_date
|
||||||
voucher_type = entry.voucher_type
|
voucher_type = entry.voucher_type
|
||||||
|
|
||||||
|
if not tax_withholding_category:
|
||||||
|
tax_withholding_category = supplier_map.get(supplier, {}).get('tax_withholding_category')
|
||||||
|
rate = tax_rate_map.get(tax_withholding_category)
|
||||||
|
|
||||||
if entry.account in tds_accounts:
|
if entry.account in tds_accounts:
|
||||||
tds_deducted += (entry.credit - entry.debit)
|
tds_deducted += (entry.credit - entry.debit)
|
||||||
|
|
||||||
total_amount_credited += (entry.credit - entry.debit)
|
total_amount_credited += (entry.credit - entry.debit)
|
||||||
|
|
||||||
if rate and tds_deducted:
|
if tds_deducted:
|
||||||
row = {
|
row = {
|
||||||
'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier, {}).get('pan'),
|
'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier, {}).get('pan'),
|
||||||
'supplier': supplier_map.get(supplier, {}).get('name')
|
'supplier': supplier_map.get(supplier, {}).get('name')
|
||||||
@ -67,7 +71,7 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map):
|
|||||||
|
|
||||||
def get_supplier_pan_map():
|
def get_supplier_pan_map():
|
||||||
supplier_map = frappe._dict()
|
supplier_map = frappe._dict()
|
||||||
suppliers = frappe.db.get_all('Supplier', fields=['name', 'pan', 'supplier_type', 'supplier_name'])
|
suppliers = frappe.db.get_all('Supplier', fields=['name', 'pan', 'supplier_type', 'supplier_name', 'tax_withholding_category'])
|
||||||
|
|
||||||
for d in suppliers:
|
for d in suppliers:
|
||||||
supplier_map[d.name] = d
|
supplier_map[d.name] = d
|
||||||
|
@ -1,116 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// rename this file from _test_[name] to test_[name] to activate
|
|
||||||
// and remove above this line
|
|
||||||
|
|
||||||
QUnit.test("test: Crop", function (assert) {
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
// number of asserts
|
|
||||||
assert.expect(2);
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
// insert a new Item
|
|
||||||
() => frappe.tests.make('Item', [
|
|
||||||
// values to be set
|
|
||||||
{item_code: 'Basil Seeds'},
|
|
||||||
{item_name: 'Basil Seeds'},
|
|
||||||
{item_group: 'Seed'}
|
|
||||||
]),
|
|
||||||
// insert a new Item
|
|
||||||
() => frappe.tests.make('Item', [
|
|
||||||
// values to be set
|
|
||||||
{item_code: 'Twigs'},
|
|
||||||
{item_name: 'Twigs'},
|
|
||||||
{item_group: 'By-product'}
|
|
||||||
]),
|
|
||||||
// insert a new Item
|
|
||||||
() => frappe.tests.make('Item', [
|
|
||||||
// values to be set
|
|
||||||
{item_code: 'Basil Leaves'},
|
|
||||||
{item_name: 'Basil Leaves'},
|
|
||||||
{item_group: 'Produce'}
|
|
||||||
]),
|
|
||||||
// insert a new Crop
|
|
||||||
() => frappe.tests.make('Crop', [
|
|
||||||
// values to be set
|
|
||||||
{title: 'Basil from seed'},
|
|
||||||
{crop_name: 'Basil'},
|
|
||||||
{scientific_name: 'Ocimum basilicum'},
|
|
||||||
{materials_required: [
|
|
||||||
[
|
|
||||||
{item_code: 'Basil Seeds'},
|
|
||||||
{qty: '25'},
|
|
||||||
{uom: 'Nos'},
|
|
||||||
{rate: '1'}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{item_code: 'Urea'},
|
|
||||||
{qty: '5'},
|
|
||||||
{uom: 'Kg'},
|
|
||||||
{rate: '10'}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{byproducts: [
|
|
||||||
[
|
|
||||||
{item_code: 'Twigs'},
|
|
||||||
{qty: '25'},
|
|
||||||
{uom: 'Nos'},
|
|
||||||
{rate: '1'}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{produce: [
|
|
||||||
[
|
|
||||||
{item_code: 'Basil Leaves'},
|
|
||||||
{qty: '100'},
|
|
||||||
{uom: 'Nos'},
|
|
||||||
{rate: '1'}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{agriculture_task: [
|
|
||||||
[
|
|
||||||
{task_name: "Plough the field"},
|
|
||||||
{start_day: 1},
|
|
||||||
{end_day: 1},
|
|
||||||
{holiday_management: "Ignore holidays"}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{task_name: "Plant the seeds"},
|
|
||||||
{start_day: 2},
|
|
||||||
{end_day: 3},
|
|
||||||
{holiday_management: "Ignore holidays"}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{task_name: "Water the field"},
|
|
||||||
{start_day: 4},
|
|
||||||
{end_day: 4},
|
|
||||||
{holiday_management: "Ignore holidays"}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{task_name: "First harvest"},
|
|
||||||
{start_day: 8},
|
|
||||||
{end_day: 8},
|
|
||||||
{holiday_management: "Ignore holidays"}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{task_name: "Add the fertilizer"},
|
|
||||||
{start_day: 10},
|
|
||||||
{end_day: 12},
|
|
||||||
{holiday_management: "Ignore holidays"}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{task_name: "Final cut"},
|
|
||||||
{start_day: 15},
|
|
||||||
{end_day: 15},
|
|
||||||
{holiday_management: "Ignore holidays"}
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]),
|
|
||||||
// agriculture task list
|
|
||||||
() => {
|
|
||||||
assert.equal(cur_frm.doc.name, 'Basil from seed');
|
|
||||||
assert.equal(cur_frm.doc.period, 15);
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
|
|
||||||
});
|
|
@ -1,34 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// rename this file from _test_[name] to test_[name] to activate
|
|
||||||
// and remove above this line
|
|
||||||
|
|
||||||
QUnit.test("test: Crop Cycle", function (assert) {
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
// number of asserts
|
|
||||||
assert.expect(1);
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
// insert a new Crop Cycle
|
|
||||||
() => frappe.tests.make('Crop Cycle', [
|
|
||||||
// values to be set
|
|
||||||
{title: 'Basil from seed 2017'},
|
|
||||||
{detected_disease: [
|
|
||||||
[
|
|
||||||
{start_date: '2017-11-21'},
|
|
||||||
{disease: 'Aphids'}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{linked_land_unit: [
|
|
||||||
[
|
|
||||||
{land_unit: 'Basil Farm'}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{crop: 'Basil from seed'},
|
|
||||||
{start_date: '2017-11-11'},
|
|
||||||
{cycle_type: 'Less than a year'}
|
|
||||||
]),
|
|
||||||
() => assert.equal(cur_frm.doc.name, 'Basil from seed 2017'),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,38 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// rename this file from _test_[name] to test_[name] to activate
|
|
||||||
// and remove above this line
|
|
||||||
|
|
||||||
QUnit.test("test: Disease", function (assert) {
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
// number of asserts
|
|
||||||
assert.expect(1);
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
// insert a new Disease
|
|
||||||
() => frappe.tests.make('Disease', [
|
|
||||||
// values to be set
|
|
||||||
{common_name: 'Aphids'},
|
|
||||||
{scientific_name: 'Aphidoidea'},
|
|
||||||
{treatment_task: [
|
|
||||||
[
|
|
||||||
{task_name: "Survey and find the aphid locations"},
|
|
||||||
{start_day: 1},
|
|
||||||
{end_day: 2},
|
|
||||||
{holiday_management: "Ignore holidays"}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{task_name: "Apply Pesticides"},
|
|
||||||
{start_day: 3},
|
|
||||||
{end_day: 3},
|
|
||||||
{holiday_management: "Ignore holidays"}
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]),
|
|
||||||
() => {
|
|
||||||
assert.equal(cur_frm.doc.treatment_period, 3);
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
|
|
||||||
});
|
|
@ -1,31 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// rename this file from _test_[name] to test_[name] to activate
|
|
||||||
// and remove above this line
|
|
||||||
|
|
||||||
QUnit.test("test: Fertilizer", function (assert) {
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
// number of asserts
|
|
||||||
assert.expect(1);
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
// insert a new Item
|
|
||||||
() => frappe.tests.make('Item', [
|
|
||||||
// values to be set
|
|
||||||
{item_code: 'Urea'},
|
|
||||||
{item_name: 'Urea'},
|
|
||||||
{item_group: 'Fertilizer'}
|
|
||||||
]),
|
|
||||||
// insert a new Fertilizer
|
|
||||||
() => frappe.tests.make('Fertilizer', [
|
|
||||||
// values to be set
|
|
||||||
{fertilizer_name: 'Urea'},
|
|
||||||
{item: 'Urea'}
|
|
||||||
]),
|
|
||||||
() => {
|
|
||||||
assert.equal(cur_frm.doc.name, 'Urea');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
|
|
||||||
});
|
|
@ -1,26 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// rename this file from _test_[name] to test_[name] to activate
|
|
||||||
// and remove above this line
|
|
||||||
|
|
||||||
QUnit.test("test: Soil Texture", function (assert) {
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
// number of asserts
|
|
||||||
assert.expect(2);
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
// insert a new Soil Texture
|
|
||||||
() => frappe.tests.make('Soil Texture', [
|
|
||||||
// values to be set
|
|
||||||
{location: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[72.882185,19.076395]}}]}'},
|
|
||||||
{collection_datetime: '2017-11-08'},
|
|
||||||
{clay_composition: 20},
|
|
||||||
{sand_composition: 30}
|
|
||||||
]),
|
|
||||||
() => {
|
|
||||||
assert.equal(cur_frm.doc.silt_composition, 50);
|
|
||||||
assert.equal(cur_frm.doc.soil_type, 'Silt Loam');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,25 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// rename this file from _test_[name] to test_[name] to activate
|
|
||||||
// and remove above this line
|
|
||||||
|
|
||||||
QUnit.test("test: Water Analysis", function (assert) {
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
// number of asserts
|
|
||||||
assert.expect(1);
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
// insert a new Water Analysis
|
|
||||||
() => frappe.tests.make('Water Analysis', [
|
|
||||||
// values to be set
|
|
||||||
{location: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[72.882185,19.076395]}}]}'},
|
|
||||||
{collection_datetime: '2017-11-08 18:43:57'},
|
|
||||||
{laboratory_testing_datetime: '2017-11-10 18:43:57'}
|
|
||||||
]),
|
|
||||||
() => {
|
|
||||||
assert.equal(cur_frm.doc.result_datetime, '2017-11-10 18:43:57');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
|
|
||||||
});
|
|
@ -110,7 +110,7 @@ frappe.ui.form.on('Asset', {
|
|||||||
|
|
||||||
if (frm.doc.status != 'Fully Depreciated') {
|
if (frm.doc.status != 'Fully Depreciated') {
|
||||||
frm.add_custom_button(__("Adjust Asset Value"), function() {
|
frm.add_custom_button(__("Adjust Asset Value"), function() {
|
||||||
frm.trigger("create_asset_adjustment");
|
frm.trigger("create_asset_value_adjustment");
|
||||||
}, __("Manage"));
|
}, __("Manage"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,14 +322,14 @@ frappe.ui.form.on('Asset', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
create_asset_adjustment: function(frm) {
|
create_asset_value_adjustment: function(frm) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
args: {
|
args: {
|
||||||
"asset": frm.doc.name,
|
"asset": frm.doc.name,
|
||||||
"asset_category": frm.doc.asset_category,
|
"asset_category": frm.doc.asset_category,
|
||||||
"company": frm.doc.company
|
"company": frm.doc.company
|
||||||
},
|
},
|
||||||
method: "erpnext.assets.doctype.asset.asset.create_asset_adjustment",
|
method: "erpnext.assets.doctype.asset.asset.create_asset_value_adjustment",
|
||||||
freeze: 1,
|
freeze: 1,
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
var doclist = frappe.model.sync(r.message);
|
var doclist = frappe.model.sync(r.message);
|
||||||
|
@ -185,83 +185,84 @@ class Asset(AccountsController):
|
|||||||
if not self.available_for_use_date:
|
if not self.available_for_use_date:
|
||||||
return
|
return
|
||||||
|
|
||||||
for d in self.get('finance_books'):
|
|
||||||
self.validate_asset_finance_books(d)
|
|
||||||
|
|
||||||
start = self.clear_depreciation_schedule()
|
start = self.clear_depreciation_schedule()
|
||||||
|
|
||||||
|
for finance_book in self.get('finance_books'):
|
||||||
|
self.validate_asset_finance_books(finance_book)
|
||||||
|
|
||||||
# value_after_depreciation - current Asset value
|
# value_after_depreciation - current Asset value
|
||||||
if self.docstatus == 1 and d.value_after_depreciation:
|
if self.docstatus == 1 and finance_book.value_after_depreciation:
|
||||||
value_after_depreciation = flt(d.value_after_depreciation)
|
value_after_depreciation = flt(finance_book.value_after_depreciation)
|
||||||
else:
|
else:
|
||||||
value_after_depreciation = (flt(self.gross_purchase_amount) -
|
value_after_depreciation = (flt(self.gross_purchase_amount) -
|
||||||
flt(self.opening_accumulated_depreciation))
|
flt(self.opening_accumulated_depreciation))
|
||||||
|
|
||||||
d.value_after_depreciation = value_after_depreciation
|
finance_book.value_after_depreciation = value_after_depreciation
|
||||||
|
|
||||||
number_of_pending_depreciations = cint(d.total_number_of_depreciations) - \
|
number_of_pending_depreciations = cint(finance_book.total_number_of_depreciations) - \
|
||||||
cint(self.number_of_depreciations_booked)
|
cint(self.number_of_depreciations_booked)
|
||||||
|
|
||||||
has_pro_rata = self.check_is_pro_rata(d)
|
has_pro_rata = self.check_is_pro_rata(finance_book)
|
||||||
|
|
||||||
if has_pro_rata:
|
if has_pro_rata:
|
||||||
number_of_pending_depreciations += 1
|
number_of_pending_depreciations += 1
|
||||||
|
|
||||||
skip_row = False
|
skip_row = False
|
||||||
for n in range(start, number_of_pending_depreciations):
|
|
||||||
|
for n in range(start[finance_book.idx-1], number_of_pending_depreciations):
|
||||||
# If depreciation is already completed (for double declining balance)
|
# If depreciation is already completed (for double declining balance)
|
||||||
if skip_row: continue
|
if skip_row: continue
|
||||||
|
|
||||||
depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d)
|
depreciation_amount = get_depreciation_amount(self, value_after_depreciation, finance_book)
|
||||||
|
|
||||||
if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
|
if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
|
||||||
schedule_date = add_months(d.depreciation_start_date,
|
schedule_date = add_months(finance_book.depreciation_start_date,
|
||||||
n * cint(d.frequency_of_depreciation))
|
n * cint(finance_book.frequency_of_depreciation))
|
||||||
|
|
||||||
# schedule date will be a year later from start date
|
# schedule date will be a year later from start date
|
||||||
# so monthly schedule date is calculated by removing 11 months from it
|
# so monthly schedule date is calculated by removing 11 months from it
|
||||||
monthly_schedule_date = add_months(schedule_date, - d.frequency_of_depreciation + 1)
|
monthly_schedule_date = add_months(schedule_date, - finance_book.frequency_of_depreciation + 1)
|
||||||
|
|
||||||
# if asset is being sold
|
# if asset is being sold
|
||||||
if date_of_sale:
|
if date_of_sale:
|
||||||
from_date = self.get_from_date(d.finance_book)
|
from_date = self.get_from_date(finance_book.finance_book)
|
||||||
depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
|
depreciation_amount, days, months = self.get_pro_rata_amt(finance_book, depreciation_amount,
|
||||||
from_date, date_of_sale)
|
from_date, date_of_sale)
|
||||||
|
|
||||||
if depreciation_amount > 0:
|
if depreciation_amount > 0:
|
||||||
self.append("schedules", {
|
self.append("schedules", {
|
||||||
"schedule_date": date_of_sale,
|
"schedule_date": date_of_sale,
|
||||||
"depreciation_amount": depreciation_amount,
|
"depreciation_amount": depreciation_amount,
|
||||||
"depreciation_method": d.depreciation_method,
|
"depreciation_method": finance_book.depreciation_method,
|
||||||
"finance_book": d.finance_book,
|
"finance_book": finance_book.finance_book,
|
||||||
"finance_book_id": d.idx
|
"finance_book_id": finance_book.idx
|
||||||
})
|
})
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
# For first row
|
# For first row
|
||||||
if has_pro_rata and not self.opening_accumulated_depreciation and n==0:
|
if has_pro_rata and not self.opening_accumulated_depreciation and n==0:
|
||||||
depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
|
depreciation_amount, days, months = self.get_pro_rata_amt(finance_book, depreciation_amount,
|
||||||
self.available_for_use_date, d.depreciation_start_date)
|
self.available_for_use_date, finance_book.depreciation_start_date)
|
||||||
|
|
||||||
# For first depr schedule date will be the start date
|
# For first depr schedule date will be the start date
|
||||||
# so monthly schedule date is calculated by removing month difference between use date and start date
|
# so monthly schedule date is calculated by removing month difference between use date and start date
|
||||||
monthly_schedule_date = add_months(d.depreciation_start_date, - months + 1)
|
monthly_schedule_date = add_months(finance_book.depreciation_start_date, - months + 1)
|
||||||
|
|
||||||
# For last row
|
# For last row
|
||||||
elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
|
elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
|
||||||
if not self.flags.increase_in_asset_life:
|
if not self.flags.increase_in_asset_life:
|
||||||
# In case of increase_in_asset_life, the self.to_date is already set on asset_repair submission
|
# In case of increase_in_asset_life, the self.to_date is already set on asset_repair submission
|
||||||
self.to_date = add_months(self.available_for_use_date,
|
self.to_date = add_months(self.available_for_use_date,
|
||||||
(n + self.number_of_depreciations_booked) * cint(d.frequency_of_depreciation))
|
(n + self.number_of_depreciations_booked) * cint(finance_book.frequency_of_depreciation))
|
||||||
|
|
||||||
depreciation_amount_without_pro_rata = depreciation_amount
|
depreciation_amount_without_pro_rata = depreciation_amount
|
||||||
|
|
||||||
depreciation_amount, days, months = self.get_pro_rata_amt(d,
|
depreciation_amount, days, months = self.get_pro_rata_amt(finance_book,
|
||||||
depreciation_amount, schedule_date, self.to_date)
|
depreciation_amount, schedule_date, self.to_date)
|
||||||
|
|
||||||
depreciation_amount = self.get_adjusted_depreciation_amount(depreciation_amount_without_pro_rata,
|
depreciation_amount = self.get_adjusted_depreciation_amount(depreciation_amount_without_pro_rata,
|
||||||
depreciation_amount, d.finance_book)
|
depreciation_amount, finance_book.finance_book)
|
||||||
|
|
||||||
monthly_schedule_date = add_months(schedule_date, 1)
|
monthly_schedule_date = add_months(schedule_date, 1)
|
||||||
schedule_date = add_days(schedule_date, days)
|
schedule_date = add_days(schedule_date, days)
|
||||||
@ -272,10 +273,10 @@ class Asset(AccountsController):
|
|||||||
self.precision("gross_purchase_amount"))
|
self.precision("gross_purchase_amount"))
|
||||||
|
|
||||||
# Adjust depreciation amount in the last period based on the expected value after useful life
|
# Adjust depreciation amount in the last period based on the expected value after useful life
|
||||||
if d.expected_value_after_useful_life and ((n == cint(number_of_pending_depreciations) - 1
|
if finance_book.expected_value_after_useful_life and ((n == cint(number_of_pending_depreciations) - 1
|
||||||
and value_after_depreciation != d.expected_value_after_useful_life)
|
and value_after_depreciation != finance_book.expected_value_after_useful_life)
|
||||||
or value_after_depreciation < d.expected_value_after_useful_life):
|
or value_after_depreciation < finance_book.expected_value_after_useful_life):
|
||||||
depreciation_amount += (value_after_depreciation - d.expected_value_after_useful_life)
|
depreciation_amount += (value_after_depreciation - finance_book.expected_value_after_useful_life)
|
||||||
skip_row = True
|
skip_row = True
|
||||||
|
|
||||||
if depreciation_amount > 0:
|
if depreciation_amount > 0:
|
||||||
@ -285,7 +286,7 @@ class Asset(AccountsController):
|
|||||||
# In pro rata case, for first and last depreciation, month range would be different
|
# In pro rata case, for first and last depreciation, month range would be different
|
||||||
month_range = months \
|
month_range = months \
|
||||||
if (has_pro_rata and n==0) or (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) \
|
if (has_pro_rata and n==0) or (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) \
|
||||||
else d.frequency_of_depreciation
|
else finance_book.frequency_of_depreciation
|
||||||
|
|
||||||
for r in range(month_range):
|
for r in range(month_range):
|
||||||
if (has_pro_rata and n == 0):
|
if (has_pro_rata and n == 0):
|
||||||
@ -311,27 +312,52 @@ class Asset(AccountsController):
|
|||||||
self.append("schedules", {
|
self.append("schedules", {
|
||||||
"schedule_date": date,
|
"schedule_date": date,
|
||||||
"depreciation_amount": amount,
|
"depreciation_amount": amount,
|
||||||
"depreciation_method": d.depreciation_method,
|
"depreciation_method": finance_book.depreciation_method,
|
||||||
"finance_book": d.finance_book,
|
"finance_book": finance_book.finance_book,
|
||||||
"finance_book_id": d.idx
|
"finance_book_id": finance_book.idx
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
self.append("schedules", {
|
self.append("schedules", {
|
||||||
"schedule_date": schedule_date,
|
"schedule_date": schedule_date,
|
||||||
"depreciation_amount": depreciation_amount,
|
"depreciation_amount": depreciation_amount,
|
||||||
"depreciation_method": d.depreciation_method,
|
"depreciation_method": finance_book.depreciation_method,
|
||||||
"finance_book": d.finance_book,
|
"finance_book": finance_book.finance_book,
|
||||||
"finance_book_id": d.idx
|
"finance_book_id": finance_book.idx
|
||||||
})
|
})
|
||||||
|
|
||||||
# used when depreciation schedule needs to be modified due to increase in asset life
|
# depreciation schedules need to be cleared before modification due to increase in asset life/asset sales
|
||||||
|
# JE: Journal Entry, FB: Finance Book
|
||||||
def clear_depreciation_schedule(self):
|
def clear_depreciation_schedule(self):
|
||||||
start = 0
|
start = []
|
||||||
for n in range(len(self.schedules)):
|
num_of_depreciations_completed = 0
|
||||||
if not self.schedules[n].journal_entry:
|
depr_schedule = []
|
||||||
del self.schedules[n:]
|
|
||||||
start = n
|
for schedule in self.get('schedules'):
|
||||||
break
|
|
||||||
|
# to update start when there are JEs linked with all the schedule rows corresponding to an FB
|
||||||
|
if len(start) == (int(schedule.finance_book_id) - 2):
|
||||||
|
start.append(num_of_depreciations_completed)
|
||||||
|
num_of_depreciations_completed = 0
|
||||||
|
|
||||||
|
# to ensure that start will only be updated once for each FB
|
||||||
|
if len(start) == (int(schedule.finance_book_id) - 1):
|
||||||
|
if schedule.journal_entry:
|
||||||
|
num_of_depreciations_completed += 1
|
||||||
|
depr_schedule.append(schedule)
|
||||||
|
else:
|
||||||
|
start.append(num_of_depreciations_completed)
|
||||||
|
num_of_depreciations_completed = 0
|
||||||
|
|
||||||
|
# to update start when all the schedule rows corresponding to the last FB are linked with JEs
|
||||||
|
if len(start) == (len(self.finance_books) - 1):
|
||||||
|
start.append(num_of_depreciations_completed)
|
||||||
|
|
||||||
|
# when the Depreciation Schedule is being created for the first time
|
||||||
|
if start == []:
|
||||||
|
start = [0] * len(self.finance_books)
|
||||||
|
else:
|
||||||
|
self.schedules = depr_schedule
|
||||||
|
|
||||||
return start
|
return start
|
||||||
|
|
||||||
def get_from_date(self, finance_book):
|
def get_from_date(self, finance_book):
|
||||||
@ -469,7 +495,6 @@ class Asset(AccountsController):
|
|||||||
|
|
||||||
asset_value_after_full_schedule = flt(
|
asset_value_after_full_schedule = flt(
|
||||||
flt(self.gross_purchase_amount) -
|
flt(self.gross_purchase_amount) -
|
||||||
flt(self.opening_accumulated_depreciation) -
|
|
||||||
flt(accumulated_depreciation_after_full_schedule), self.precision('gross_purchase_amount'))
|
flt(accumulated_depreciation_after_full_schedule), self.precision('gross_purchase_amount'))
|
||||||
|
|
||||||
if (row.expected_value_after_useful_life and
|
if (row.expected_value_after_useful_life and
|
||||||
@ -731,14 +756,14 @@ def create_asset_repair(asset, asset_name):
|
|||||||
return asset_repair
|
return asset_repair
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_asset_adjustment(asset, asset_category, company):
|
def create_asset_value_adjustment(asset, asset_category, company):
|
||||||
asset_maintenance = frappe.get_doc("Asset Value Adjustment")
|
asset_value_adjustment = frappe.new_doc("Asset Value Adjustment")
|
||||||
asset_maintenance.update({
|
asset_value_adjustment.update({
|
||||||
"asset": asset,
|
"asset": asset,
|
||||||
"company": company,
|
"company": company,
|
||||||
"asset_category": asset_category
|
"asset_category": asset_category
|
||||||
})
|
})
|
||||||
return asset_maintenance
|
return asset_value_adjustment
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def transfer_asset(args):
|
def transfer_asset(args):
|
||||||
|
@ -955,6 +955,82 @@ class TestDepreciationBasics(AssetSetup):
|
|||||||
|
|
||||||
self.assertEqual(len(asset.schedules), 1)
|
self.assertEqual(len(asset.schedules), 1)
|
||||||
|
|
||||||
|
def test_clear_depreciation_schedule_for_multiple_finance_books(self):
|
||||||
|
asset = create_asset(
|
||||||
|
item_code = "Macbook Pro",
|
||||||
|
available_for_use_date = "2019-12-31",
|
||||||
|
do_not_save = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
|
asset.append("finance_books", {
|
||||||
|
"depreciation_method": "Straight Line",
|
||||||
|
"frequency_of_depreciation": 1,
|
||||||
|
"total_number_of_depreciations": 3,
|
||||||
|
"expected_value_after_useful_life": 10000,
|
||||||
|
"depreciation_start_date": "2020-01-31"
|
||||||
|
})
|
||||||
|
asset.append("finance_books", {
|
||||||
|
"depreciation_method": "Straight Line",
|
||||||
|
"frequency_of_depreciation": 1,
|
||||||
|
"total_number_of_depreciations": 6,
|
||||||
|
"expected_value_after_useful_life": 10000,
|
||||||
|
"depreciation_start_date": "2020-01-31"
|
||||||
|
})
|
||||||
|
asset.append("finance_books", {
|
||||||
|
"depreciation_method": "Straight Line",
|
||||||
|
"frequency_of_depreciation": 12,
|
||||||
|
"total_number_of_depreciations": 3,
|
||||||
|
"expected_value_after_useful_life": 10000,
|
||||||
|
"depreciation_start_date": "2020-12-31"
|
||||||
|
})
|
||||||
|
asset.submit()
|
||||||
|
|
||||||
|
post_depreciation_entries(date="2020-04-01")
|
||||||
|
asset.load_from_db()
|
||||||
|
|
||||||
|
asset.clear_depreciation_schedule()
|
||||||
|
|
||||||
|
self.assertEqual(len(asset.schedules), 6)
|
||||||
|
|
||||||
|
for schedule in asset.schedules:
|
||||||
|
if schedule.idx <= 3:
|
||||||
|
self.assertEqual(schedule.finance_book_id, "1")
|
||||||
|
else:
|
||||||
|
self.assertEqual(schedule.finance_book_id, "2")
|
||||||
|
|
||||||
|
def test_depreciation_schedules_are_set_up_for_multiple_finance_books(self):
|
||||||
|
asset = create_asset(
|
||||||
|
item_code = "Macbook Pro",
|
||||||
|
available_for_use_date = "2019-12-31",
|
||||||
|
do_not_save = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
|
asset.append("finance_books", {
|
||||||
|
"depreciation_method": "Straight Line",
|
||||||
|
"frequency_of_depreciation": 12,
|
||||||
|
"total_number_of_depreciations": 3,
|
||||||
|
"expected_value_after_useful_life": 10000,
|
||||||
|
"depreciation_start_date": "2020-12-31"
|
||||||
|
})
|
||||||
|
asset.append("finance_books", {
|
||||||
|
"depreciation_method": "Straight Line",
|
||||||
|
"frequency_of_depreciation": 12,
|
||||||
|
"total_number_of_depreciations": 6,
|
||||||
|
"expected_value_after_useful_life": 10000,
|
||||||
|
"depreciation_start_date": "2020-12-31"
|
||||||
|
})
|
||||||
|
asset.save()
|
||||||
|
|
||||||
|
self.assertEqual(len(asset.schedules), 9)
|
||||||
|
|
||||||
|
for schedule in asset.schedules:
|
||||||
|
if schedule.idx <= 3:
|
||||||
|
self.assertEqual(schedule.finance_book_id, 1)
|
||||||
|
else:
|
||||||
|
self.assertEqual(schedule.finance_book_id, 2)
|
||||||
|
|
||||||
def test_depreciation_entry_cancellation(self):
|
def test_depreciation_entry_cancellation(self):
|
||||||
asset = create_asset(
|
asset = create_asset(
|
||||||
item_code = "Macbook Pro",
|
item_code = "Macbook Pro",
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/asset",
|
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/asset",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_complete": 0,
|
"is_complete": 0,
|
||||||
"modified": "2021-08-24 17:50:41.573281",
|
"modified": "2021-12-02 11:24:37.963746",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Assets",
|
"name": "Assets",
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"is_complete": 0,
|
"is_complete": 0,
|
||||||
"is_single": 0,
|
"is_single": 0,
|
||||||
"is_skipped": 0,
|
"is_skipped": 0,
|
||||||
"modified": "2021-08-24 12:49:37.665239",
|
"modified": "2021-11-23 10:02:03.242127",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Asset Category",
|
"name": "Asset Category",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
{
|
{
|
||||||
"action": "Show Form Tour",
|
"action": "Create Entry",
|
||||||
"action_label": "Let's create a new Asset item",
|
"action_label": "Let's create a new Asset item",
|
||||||
"creation": "2021-08-13 14:27:07.277167",
|
"creation": "2021-08-13 14:27:07.277167",
|
||||||
"description": "# Asset Item\n\nAsset items are created based on Asset Category. You can create one or multiple items against once Asset Category. The sales and purchase transaction for Asset is done via Asset Item. ",
|
"description": "# Asset Item\n\nAsset items are created based on Asset Category. You can create one or multiple items against once Asset Category. The sales and purchase transaction for Asset is done via Asset Item. ",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Onboarding Step",
|
"doctype": "Onboarding Step",
|
||||||
|
"form_tour": "Item",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_complete": 0,
|
"is_complete": 0,
|
||||||
"is_single": 0,
|
"is_single": 0,
|
||||||
"is_skipped": 0,
|
"is_skipped": 0,
|
||||||
"modified": "2021-08-16 13:59:18.362233",
|
"modified": "2021-12-02 11:23:48.158504",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Asset Item",
|
"name": "Asset Item",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"reference_document": "Item",
|
"reference_document": "Item",
|
||||||
"show_form_tour": 0,
|
"show_form_tour": 1,
|
||||||
"show_full_form": 0,
|
"show_full_form": 1,
|
||||||
"title": "Create an Asset Item",
|
"title": "Create an Asset Item",
|
||||||
"validate_action": 1
|
"validate_action": 1
|
||||||
}
|
}
|
@ -9,7 +9,7 @@
|
|||||||
"is_complete": 0,
|
"is_complete": 0,
|
||||||
"is_single": 0,
|
"is_single": 0,
|
||||||
"is_skipped": 0,
|
"is_skipped": 0,
|
||||||
"modified": "2021-08-24 17:26:57.180637",
|
"modified": "2021-11-23 10:02:03.235498",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Asset Purchase",
|
"name": "Asset Purchase",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"is_complete": 0,
|
"is_complete": 0,
|
||||||
"is_single": 0,
|
"is_single": 0,
|
||||||
"is_skipped": 0,
|
"is_skipped": 0,
|
||||||
"modified": "2021-08-24 17:46:37.646174",
|
"modified": "2021-11-23 10:02:03.229566",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Fixed Asset Accounts",
|
"name": "Fixed Asset Accounts",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
|
@ -72,6 +72,7 @@ class PurchaseOrder(BuyingController):
|
|||||||
self.create_raw_materials_supplied("supplied_items")
|
self.create_raw_materials_supplied("supplied_items")
|
||||||
self.set_received_qty_for_drop_ship_items()
|
self.set_received_qty_for_drop_ship_items()
|
||||||
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_order_reference)
|
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_order_reference)
|
||||||
|
self.reset_default_field_value("set_warehouse", "items", "warehouse")
|
||||||
|
|
||||||
def validate_with_previous_doc(self):
|
def validate_with_previous_doc(self):
|
||||||
super(PurchaseOrder, self).validate_with_previous_doc({
|
super(PurchaseOrder, self).validate_with_previous_doc({
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: purchase order", function(assert) {
|
|
||||||
assert.expect(16);
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Purchase Order', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{is_subcontracted: 'No'},
|
|
||||||
{currency: 'INR'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 4'},
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 2)},
|
|
||||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
|
||||||
{"qty": 5},
|
|
||||||
{"uom": 'Unit'},
|
|
||||||
{"rate": 100},
|
|
||||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 1'},
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
|
||||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
|
||||||
{"qty": 2},
|
|
||||||
{"uom": 'Unit'},
|
|
||||||
{"rate": 100},
|
|
||||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
|
|
||||||
{tc_name: 'Test Term 1'},
|
|
||||||
{terms: 'This is a term.'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => {
|
|
||||||
// Get supplier details
|
|
||||||
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
|
|
||||||
assert.ok(cur_frm.doc.schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 1), "Schedule Date correct");
|
|
||||||
assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Contact email correct");
|
|
||||||
// Get item details
|
|
||||||
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item name correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].description == 'Test Product 4', "Description correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].qty == 5, "Quantity correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 2), "Schedule Date correct");
|
|
||||||
|
|
||||||
assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Item name correct");
|
|
||||||
assert.ok(cur_frm.doc.items[1].description == 'Test Product 1', "Description correct");
|
|
||||||
assert.ok(cur_frm.doc.items[1].qty == 2, "Quantity correct");
|
|
||||||
assert.ok(cur_frm.doc.items[1].schedule_date == cur_frm.doc.schedule_date, "Schedule Date correct");
|
|
||||||
// Calculate total
|
|
||||||
assert.ok(cur_frm.doc.total == 700, "Total correct");
|
|
||||||
// Get terms
|
|
||||||
assert.ok(cur_frm.doc.terms == 'This is a term.', "Terms correct");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => cur_frm.print_doc(),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
assert.ok($('.btn-print-print').is(':visible'), "Print Format Available");
|
|
||||||
assert.ok($('div > div:nth-child(5) > div > div > table > tbody > tr > td:nth-child(4) > div').text().includes('Test Product 4'), "Print Preview Works");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => cur_frm.print_doc(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.status == 'To Receive and Bill', "Submitted successfully");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,61 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: purchase order with get items", function(assert) {
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Purchase Order', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{is_subcontracted: 'No'},
|
|
||||||
{buying_price_list: 'Test-Buying-USD'},
|
|
||||||
{currency: 'USD'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 4'},
|
|
||||||
{"qty": 5},
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
|
||||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
|
||||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => frappe.click_button('Get items from'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
|
|
||||||
() => frappe.click_link('Product Bundle'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
|
|
||||||
() => cur_dialog.set_value('product_bundle', 'Computer'),
|
|
||||||
() => frappe.click_button('Get Items'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
|
|
||||||
// Check if items are fetched from Product Bundle
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.items[1].item_name == 'CPU', "Product bundle item 1 correct");
|
|
||||||
assert.ok(cur_frm.doc.items[2].item_name == 'Screen', "Product bundle item 2 correct");
|
|
||||||
assert.ok(cur_frm.doc.items[3].item_name == 'Keyboard', "Product bundle item 3 correct");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => cur_frm.doc.items[1].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
|
|
||||||
() => cur_frm.doc.items[2].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
|
|
||||||
() => cur_frm.doc.items[3].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
|
|
||||||
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,74 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: purchase order receipt", function(assert) {
|
|
||||||
assert.expect(5);
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Purchase Order', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{is_subcontracted: 'No'},
|
|
||||||
{buying_price_list: 'Test-Buying-USD'},
|
|
||||||
{currency: 'USD'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 1'},
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
|
||||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
|
||||||
{"qty": 5},
|
|
||||||
{"uom": 'Unit'},
|
|
||||||
{"rate": 100},
|
|
||||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => {
|
|
||||||
|
|
||||||
// Check supplier and item details
|
|
||||||
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 1', "Item name correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].description == 'Test Product 1', "Description correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].qty == 5, "Quantity correct");
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
|
|
||||||
() => frappe.timeout(1.5),
|
|
||||||
() => frappe.click_button('Close'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
|
|
||||||
// Make Purchase Receipt
|
|
||||||
() => frappe.click_button('Make'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
|
|
||||||
() => frappe.click_link('Receipt'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
|
|
||||||
() => cur_frm.save(),
|
|
||||||
|
|
||||||
// Save and submit Purchase Receipt
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
|
|
||||||
// View Purchase order in Stock Ledger
|
|
||||||
() => frappe.click_button('View'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
|
|
||||||
() => frappe.click_link('Stock Ledger'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
assert.ok($('div.slick-cell.l2.r2 > a').text().includes('Test Product 1')
|
|
||||||
&& $('div.slick-cell.l9.r9 > div').text().includes(5), "Stock ledger entry correct");
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,47 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: purchase order with discount on grand total", function(assert) {
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Purchase Order', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{is_subcontracted: 'No'},
|
|
||||||
{buying_price_list: 'Test-Buying-EUR'},
|
|
||||||
{currency: 'EUR'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 4'},
|
|
||||||
{"qty": 5},
|
|
||||||
{"uom": 'Unit'},
|
|
||||||
{"rate": 500 },
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
|
||||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
|
||||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{apply_discount_on: 'Grand Total'},
|
|
||||||
{additional_discount_percentage: 10}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].rate == 500, "Rate correct");
|
|
||||||
// Calculate total
|
|
||||||
assert.ok(cur_frm.doc.total == 2500, "Total correct");
|
|
||||||
// Calculate grand total after discount
|
|
||||||
assert.ok(cur_frm.doc.grand_total == 2250, "Grand total correct");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,44 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: purchase order with item wise discount", function(assert) {
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Purchase Order', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{is_subcontracted: 'No'},
|
|
||||||
{buying_price_list: 'Test-Buying-EUR'},
|
|
||||||
{currency: 'EUR'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 4'},
|
|
||||||
{"qty": 5},
|
|
||||||
{"uom": 'Unit'},
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
|
||||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
|
||||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))},
|
|
||||||
{"discount_percentage": 20}
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].discount_percentage == 20, "Discount correct");
|
|
||||||
// Calculate totals after discount
|
|
||||||
assert.ok(cur_frm.doc.total == 2000, "Total correct");
|
|
||||||
assert.ok(cur_frm.doc.grand_total == 2000, "Grand total correct");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,39 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: purchase order with multi UOM", function(assert) {
|
|
||||||
assert.expect(3);
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Purchase Order', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{is_subcontracted: 'No'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 4'},
|
|
||||||
{"qty": 5},
|
|
||||||
{"uom": 'Unit'},
|
|
||||||
{"rate": 100},
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
|
||||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
|
||||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item name correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].uom == 'Unit', "Multi UOM correct");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,43 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: purchase order with shipping rule", function(assert) {
|
|
||||||
assert.expect(3);
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Purchase Order', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{is_subcontracted: 'No'},
|
|
||||||
{buying_price_list: 'Test-Buying-USD'},
|
|
||||||
{currency: 'USD'},
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 4'},
|
|
||||||
{"qty": 5},
|
|
||||||
{"uom": 'Unit'},
|
|
||||||
{"rate": 500 },
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
|
||||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
|
||||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
|
|
||||||
{shipping_rule:'Two Day Shipping'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => {
|
|
||||||
// Check grand total
|
|
||||||
assert.ok(cur_frm.doc.total_taxes_and_charges == 200, "Taxes and charges correct");
|
|
||||||
assert.ok(cur_frm.doc.grand_total == 2700, "Grand total correct");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,44 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: purchase order with taxes and charges", function(assert) {
|
|
||||||
assert.expect(3);
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Purchase Order', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{is_subcontracted: 'No'},
|
|
||||||
{buying_price_list: 'Test-Buying-USD'},
|
|
||||||
{currency: 'USD'},
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 4'},
|
|
||||||
{"qty": 5},
|
|
||||||
{"uom": 'Unit'},
|
|
||||||
{"rate": 500 },
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
|
||||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
|
||||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
|
|
||||||
{taxes_and_charges: 'TEST In State GST - FT'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => {
|
|
||||||
// Check taxes and calculate grand total
|
|
||||||
assert.ok(cur_frm.doc.taxes[1].account_head=='SGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), "Account Head abbr correct");
|
|
||||||
assert.ok(cur_frm.doc.total_taxes_and_charges == 225, "Taxes and charges correct");
|
|
||||||
assert.ok(cur_frm.doc.grand_total == 2725, "Grand total correct");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -124,6 +124,14 @@ frappe.ui.form.on("Request for Quotation",{
|
|||||||
dialog.show()
|
dialog.show()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
schedule_date(frm) {
|
||||||
|
if(frm.doc.schedule_date){
|
||||||
|
frm.doc.items.forEach((item) => {
|
||||||
|
item.schedule_date = frm.doc.schedule_date;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
refresh_field("items");
|
||||||
|
},
|
||||||
preview: (frm) => {
|
preview: (frm) => {
|
||||||
let dialog = new frappe.ui.Dialog({
|
let dialog = new frappe.ui.Dialog({
|
||||||
title: __('Preview Email'),
|
title: __('Preview Email'),
|
||||||
@ -184,7 +192,13 @@ frappe.ui.form.on("Request for Quotation",{
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
frappe.ui.form.on("Request for Quotation Item", {
|
||||||
|
items_add(frm, cdt, cdn) {
|
||||||
|
if (frm.doc.schedule_date) {
|
||||||
|
frappe.model.set_value(cdt, cdn, 'schedule_date', frm.doc.schedule_date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
frappe.ui.form.on("Request for Quotation Supplier",{
|
frappe.ui.form.on("Request for Quotation Supplier",{
|
||||||
supplier: function(frm, cdt, cdn) {
|
supplier: function(frm, cdt, cdn) {
|
||||||
var d = locals[cdt][cdn]
|
var d = locals[cdt][cdn]
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"vendor",
|
"vendor",
|
||||||
"column_break1",
|
"column_break1",
|
||||||
"transaction_date",
|
"transaction_date",
|
||||||
|
"schedule_date",
|
||||||
"status",
|
"status",
|
||||||
"amended_from",
|
"amended_from",
|
||||||
"suppliers_section",
|
"suppliers_section",
|
||||||
@ -246,16 +247,22 @@
|
|||||||
"fieldname": "sec_break_email_2",
|
"fieldname": "sec_break_email_2",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hide_border": 1
|
"hide_border": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "schedule_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "Required Date"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-shopping-cart",
|
"icon": "fa fa-shopping-cart",
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-05 22:04:29.017134",
|
"modified": "2021-11-24 17:47:49.909000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Request for Quotation",
|
"name": "Request for Quotation",
|
||||||
|
"naming_rule": "By \"Naming Series\" field",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: request_for_quotation", function(assert) {
|
|
||||||
assert.expect(14);
|
|
||||||
let done = assert.async();
|
|
||||||
let date;
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
date = frappe.datetime.add_days(frappe.datetime.now_date(), 10);
|
|
||||||
return frappe.tests.make('Request for Quotation', [
|
|
||||||
{transaction_date: date},
|
|
||||||
{suppliers: [
|
|
||||||
[
|
|
||||||
{"supplier": 'Test Supplier'},
|
|
||||||
{"email_id": 'test@supplier.com'}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 4'},
|
|
||||||
{"qty": 5},
|
|
||||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(),20)},
|
|
||||||
{"warehouse": 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{message_for_supplier: 'Please supply the specified items at the best possible rates'},
|
|
||||||
{tc_name: 'Test Term 1'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => frappe.timeout(3),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.transaction_date == date, "Date correct");
|
|
||||||
assert.ok(cur_frm.doc.company == cur_frm.doc.company, "Company correct");
|
|
||||||
assert.ok(cur_frm.doc.suppliers[0].supplier_name == 'Test Supplier', "Supplier name correct");
|
|
||||||
assert.ok(cur_frm.doc.suppliers[0].contact == 'Contact 3-Test Supplier', "Contact correct");
|
|
||||||
assert.ok(cur_frm.doc.suppliers[0].email_id == 'test@supplier.com', "Email id correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item Name correct");
|
|
||||||
assert.ok(cur_frm.doc.items[0].warehouse == 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company")), "Warehouse correct");
|
|
||||||
assert.ok(cur_frm.doc.message_for_supplier == 'Please supply the specified items at the best possible rates', "Reply correct");
|
|
||||||
assert.ok(cur_frm.doc.tc_name == 'Test Term 1', "Term name correct");
|
|
||||||
},
|
|
||||||
() => frappe.timeout(3),
|
|
||||||
() => cur_frm.print_doc(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
assert.ok($('.btn-print-print').is(':visible'), "Print Format Available");
|
|
||||||
assert.ok($('.section-break+ .section-break .column-break:nth-child(1) .value').text().includes("Test Product 4"), "Print Preview Works");
|
|
||||||
},
|
|
||||||
() => cur_frm.print_doc(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Get items from'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => frappe.click_link('Material Request'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Get Items'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Getting items from material requests work");
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.docstatus == 1, "Quotation request submitted");
|
|
||||||
},
|
|
||||||
() => frappe.click_button('Send Supplier Emails'),
|
|
||||||
() => frappe.timeout(6),
|
|
||||||
() => {
|
|
||||||
assert.ok($('div.modal.fade.in > div.modal-dialog > div > div.modal-body.ui-front > div.msgprint').text().includes("Email sent to supplier Test Supplier"), "Send emails working");
|
|
||||||
},
|
|
||||||
() => frappe.click_button('Close'),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,128 +0,0 @@
|
|||||||
QUnit.module('buying');
|
|
||||||
|
|
||||||
QUnit.test("Test: Request for Quotation", function (assert) {
|
|
||||||
assert.expect(5);
|
|
||||||
let done = assert.async();
|
|
||||||
let rfq_name = "";
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
// Go to RFQ list
|
|
||||||
() => frappe.set_route("List", "Request for Quotation"),
|
|
||||||
// Create a new RFQ
|
|
||||||
() => frappe.new_doc("Request for Quotation"),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => cur_frm.set_value("transaction_date", "04-04-2017"),
|
|
||||||
() => cur_frm.set_value("company", "For Testing"),
|
|
||||||
// Add Suppliers
|
|
||||||
() => {
|
|
||||||
cur_frm.fields_dict.suppliers.grid.grid_rows[0].toggle_view();
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
cur_frm.fields_dict.suppliers.grid.grid_rows[0].doc.supplier = "_Test Supplier";
|
|
||||||
frappe.click_check('Send Email');
|
|
||||||
cur_frm.cur_grid.frm.script_manager.trigger('supplier');
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
cur_frm.cur_grid.toggle_view();
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Add Row',0),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
cur_frm.fields_dict.suppliers.grid.grid_rows[1].toggle_view();
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.supplier = "_Test Supplier 1";
|
|
||||||
frappe.click_check('Send Email');
|
|
||||||
cur_frm.cur_grid.frm.script_manager.trigger('supplier');
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
cur_frm.cur_grid.toggle_view();
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
// Add Item
|
|
||||||
() => {
|
|
||||||
cur_frm.fields_dict.items.grid.grid_rows[0].toggle_view();
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
cur_frm.fields_dict.items.grid.grid_rows[0].doc.item_code = "_Test Item";
|
|
||||||
frappe.set_control('item_code',"_Test Item");
|
|
||||||
frappe.set_control('qty',5);
|
|
||||||
frappe.set_control('schedule_date', "05-05-2017");
|
|
||||||
cur_frm.cur_grid.frm.script_manager.trigger('supplier');
|
|
||||||
},
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
cur_frm.cur_grid.toggle_view();
|
|
||||||
},
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
cur_frm.fields_dict.items.grid.grid_rows[0].doc.warehouse = "_Test Warehouse - FT";
|
|
||||||
},
|
|
||||||
() => frappe.click_button('Save'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Submit'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Yes'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Menu'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_link('Reload'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
assert.equal(cur_frm.doc.docstatus, 1);
|
|
||||||
rfq_name = cur_frm.doc.name;
|
|
||||||
assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[0].doc.quote_status == "Pending");
|
|
||||||
assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.quote_status == "Pending");
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
cur_frm.fields_dict.suppliers.grid.grid_rows[0].toggle_view();
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
cur_frm.cur_grid.toggle_view();
|
|
||||||
},
|
|
||||||
() => frappe.click_button('Update'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
|
|
||||||
() => frappe.click_button('Supplier Quotation'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_link('Make'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
frappe.set_control('supplier',"_Test Supplier 1");
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Make Supplier Quotation'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => cur_frm.set_value("company", "For Testing"),
|
|
||||||
() => cur_frm.fields_dict.items.grid.grid_rows[0].doc.rate = 4.99,
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Save'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Submit'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Yes'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.set_route("List", "Request for Quotation"),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => frappe.set_route("List", "Request for Quotation"),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => frappe.click_link(rfq_name),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Menu'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_link('Reload'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.quote_status == "Received");
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,77 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: supplier", function(assert) {
|
|
||||||
assert.expect(6);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Supplier', [
|
|
||||||
{supplier_name: 'Test Supplier'},
|
|
||||||
{supplier_group: 'Hardware'},
|
|
||||||
{country: 'India'},
|
|
||||||
{default_currency: 'INR'},
|
|
||||||
{accounts: [
|
|
||||||
[
|
|
||||||
{'company': "For Testing"},
|
|
||||||
{'account': "Creditors - FT"}
|
|
||||||
]]
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('New Address'),
|
|
||||||
() => {
|
|
||||||
return frappe.tests.set_form_values(cur_frm, [
|
|
||||||
{address_title:"Test3"},
|
|
||||||
{address_type: "Billing"},
|
|
||||||
{address_line1: "Billing Street 3"},
|
|
||||||
{city: "Billing City 3"},
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => frappe.click_button('New Address'),
|
|
||||||
() => {
|
|
||||||
return frappe.tests.set_form_values(cur_frm, [
|
|
||||||
{address_title:"Test3"},
|
|
||||||
{address_type: "Shipping"},
|
|
||||||
{address_line1: "Shipping Street 3"},
|
|
||||||
{city: "Shipping City 3"},
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => frappe.click_button('New Address'),
|
|
||||||
() => {
|
|
||||||
return frappe.tests.set_form_values(cur_frm, [
|
|
||||||
{address_title:"Test3"},
|
|
||||||
{address_type: "Warehouse"},
|
|
||||||
{address_line1: "Warehouse Street 3"},
|
|
||||||
{city: "Warehouse City 3"},
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => frappe.click_button('New Contact'),
|
|
||||||
() => {
|
|
||||||
return frappe.tests.set_form_values(cur_frm, [
|
|
||||||
{first_name: "Contact 3"},
|
|
||||||
{email_id: "test@supplier.com"}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.set_route('Form', 'Supplier', 'Test Supplier'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Name correct");
|
|
||||||
assert.ok(cur_frm.doc.supplier_group == 'Hardware', "Type correct");
|
|
||||||
assert.ok(cur_frm.doc.default_currency == 'INR', "Currency correct");
|
|
||||||
assert.ok(cur_frm.doc.accounts[0].account == 'Creditors - '+frappe.get_abbr('For Testing'), " Account Head abbr correct");
|
|
||||||
assert.ok($('.address-box:nth-child(3) p').text().includes('Shipping City 3'), "Address correct");
|
|
||||||
assert.ok($('.col-sm-6+ .col-sm-6 .h6').text().includes('Contact 3'), "Contact correct");
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -17,6 +17,7 @@
|
|||||||
"company",
|
"company",
|
||||||
"transaction_date",
|
"transaction_date",
|
||||||
"valid_till",
|
"valid_till",
|
||||||
|
"quotation_number",
|
||||||
"amended_from",
|
"amended_from",
|
||||||
"address_section",
|
"address_section",
|
||||||
"supplier_address",
|
"supplier_address",
|
||||||
@ -797,6 +798,11 @@
|
|||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Valid Till"
|
"label": "Valid Till"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "quotation_number",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Quotation Number"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-shopping-cart",
|
"icon": "fa fa-shopping-cart",
|
||||||
@ -804,10 +810,11 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-04-19 00:58:20.995491",
|
"modified": "2021-12-11 06:43:20.924080",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Supplier Quotation",
|
"name": "Supplier Quotation",
|
||||||
|
"naming_rule": "By \"Naming Series\" field",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: supplier quotation", function(assert) {
|
|
||||||
assert.expect(11);
|
|
||||||
let done = assert.async();
|
|
||||||
let date;
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
date = frappe.datetime.add_days(frappe.datetime.now_date(), 10);
|
|
||||||
return frappe.tests.make('Supplier Quotation', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{transaction_date: date},
|
|
||||||
{currency: 'INR'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 4'},
|
|
||||||
{"qty": 5},
|
|
||||||
{"uom": 'Unit'},
|
|
||||||
{"rate": 200},
|
|
||||||
{"warehouse": 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{apply_discount_on: 'Grand Total'},
|
|
||||||
{additional_discount_percentage: 10},
|
|
||||||
{tc_name: 'Test Term 1'},
|
|
||||||
{terms: 'This is a term'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => frappe.timeout(3),
|
|
||||||
() => {
|
|
||||||
// Get Supplier details
|
|
||||||
assert.ok(cur_frm.doc.supplier == 'Test Supplier', "Supplier correct");
|
|
||||||
assert.ok(cur_frm.doc.company == cur_frm.doc.company, "Company correct");
|
|
||||||
// Get Contact details
|
|
||||||
assert.ok(cur_frm.doc.contact_person == 'Contact 3-Test Supplier', "Conatct correct");
|
|
||||||
assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Email correct");
|
|
||||||
// Get uom
|
|
||||||
assert.ok(cur_frm.doc.items[0].uom == 'Unit', "Multi uom correct");
|
|
||||||
assert.ok(cur_frm.doc.total == 1000, "Total correct");
|
|
||||||
// Calculate total after discount
|
|
||||||
assert.ok(cur_frm.doc.grand_total == 900, "Grand total correct");
|
|
||||||
// Get terms
|
|
||||||
assert.ok(cur_frm.doc.tc_name == 'Test Term 1', "Terms correct");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => cur_frm.print_doc(),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
assert.ok($('.btn-print-print').is(':visible'), "Print Format Available");
|
|
||||||
assert.ok($("table > tbody > tr > td:nth-child(3) > div").text().includes("Test Product 4"), "Print Preview Works As Expected");
|
|
||||||
},
|
|
||||||
() => cur_frm.print_doc(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('Get items from'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => frappe.click_link('Material Request'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => frappe.click_button('Get Items'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
// Get item from Material Requests
|
|
||||||
assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Getting items from material requests work");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,34 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: supplier quotation with item wise discount", function(assert){
|
|
||||||
assert.expect(2);
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Supplier Quotation', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{company: 'For Testing'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 4'},
|
|
||||||
{"qty": 5},
|
|
||||||
{"uom": 'Unit'},
|
|
||||||
{"warehouse": 'All Warehouses - FT'},
|
|
||||||
{'discount_percentage': 10},
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.total == 900, "Total correct");
|
|
||||||
assert.ok(cur_frm.doc.grand_total == 900, "Grand total correct");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,37 +0,0 @@
|
|||||||
QUnit.module('Buying');
|
|
||||||
|
|
||||||
QUnit.test("test: supplier quotation with taxes and charges", function(assert) {
|
|
||||||
assert.expect(3);
|
|
||||||
let done = assert.async();
|
|
||||||
let supplier_quotation_name;
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Supplier Quotation', [
|
|
||||||
{supplier: 'Test Supplier'},
|
|
||||||
{items: [
|
|
||||||
[
|
|
||||||
{"item_code": 'Test Product 4'},
|
|
||||||
{"qty": 5},
|
|
||||||
{"rate": 100},
|
|
||||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
|
|
||||||
]
|
|
||||||
]},
|
|
||||||
{taxes_and_charges:'TEST In State GST - FT'},
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {supplier_quotation_name = cur_frm.doc.name;},
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
|
|
||||||
assert.ok(cur_frm.doc.total_taxes_and_charges == 45, "Taxes and charges correct");
|
|
||||||
assert.ok(cur_frm.doc.grand_total == 545, "Grand total correct");
|
|
||||||
},
|
|
||||||
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
22
erpnext/controllers/tests/test_transaction_base.py
Normal file
22
erpnext/controllers/tests/test_transaction_base.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
class TestUtils(unittest.TestCase):
|
||||||
|
def test_reset_default_field_value(self):
|
||||||
|
doc = frappe.get_doc({
|
||||||
|
"doctype": "Purchase Receipt",
|
||||||
|
"set_warehouse": "Warehouse 1",
|
||||||
|
})
|
||||||
|
|
||||||
|
# Same values
|
||||||
|
doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}]
|
||||||
|
doc.reset_default_field_value("set_warehouse", "items", "warehouse")
|
||||||
|
self.assertEqual(doc.set_warehouse, "Warehouse 1")
|
||||||
|
|
||||||
|
# Mixed values
|
||||||
|
doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 2"}, {"warehouse": "Warehouse 1"}]
|
||||||
|
doc.reset_default_field_value("set_warehouse", "items", "warehouse")
|
||||||
|
self.assertEqual(doc.set_warehouse, None)
|
||||||
|
|
@ -91,8 +91,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"migration_hash": "3ae78b12dd1c64d551736c6e82092f90",
|
"modified": "2021-11-03 10:00:36.883496",
|
||||||
"modified": "2021-11-03 09:00:36.883496",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "CRM Settings",
|
"name": "CRM Settings",
|
||||||
|
@ -8,7 +8,6 @@ from frappe.contacts.address_and_contact import load_address_and_contact
|
|||||||
from frappe.email.inbox import link_communication_to_document
|
from frappe.email.inbox import link_communication_to_document
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from frappe.utils import (
|
from frappe.utils import (
|
||||||
cint,
|
|
||||||
comma_and,
|
comma_and,
|
||||||
cstr,
|
cstr,
|
||||||
get_link_to_form,
|
get_link_to_form,
|
||||||
@ -39,11 +38,7 @@ class Lead(SellingController):
|
|||||||
self.check_email_id_is_unique()
|
self.check_email_id_is_unique()
|
||||||
self.validate_email_id()
|
self.validate_email_id()
|
||||||
self.validate_contact_date()
|
self.validate_contact_date()
|
||||||
self._prev = frappe._dict({
|
self.set_prev()
|
||||||
"contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if (not cint(self.is_new())) else None,
|
|
||||||
"ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if (not cint(self.is_new())) else None,
|
|
||||||
"contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if (not cint(self.is_new())) else None,
|
|
||||||
})
|
|
||||||
|
|
||||||
def set_full_name(self):
|
def set_full_name(self):
|
||||||
if self.first_name:
|
if self.first_name:
|
||||||
@ -75,6 +70,16 @@ class Lead(SellingController):
|
|||||||
self.add_calendar_event()
|
self.add_calendar_event()
|
||||||
self.update_prospects()
|
self.update_prospects()
|
||||||
|
|
||||||
|
def set_prev(self):
|
||||||
|
if self.is_new():
|
||||||
|
self._prev = frappe._dict({
|
||||||
|
"contact_date": None,
|
||||||
|
"ends_on": None,
|
||||||
|
"contact_by": None
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
self._prev = frappe.db.get_value("Lead", self.name, ["contact_date", "ends_on", "contact_by"], as_dict=1)
|
||||||
|
|
||||||
def before_insert(self):
|
def before_insert(self):
|
||||||
self.contact_doc = self.create_contact()
|
self.contact_doc = self.create_contact()
|
||||||
|
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
QUnit.module("sales");
|
|
||||||
|
|
||||||
QUnit.test("test: lead", function (assert) {
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
let lead_name = frappe.utils.get_random(10);
|
|
||||||
frappe.run_serially([
|
|
||||||
// test lead creation
|
|
||||||
() => frappe.set_route("List", "Lead"),
|
|
||||||
() => frappe.new_doc("Lead"),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => cur_frm.set_value("lead_name", lead_name),
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.lead_name.includes(lead_name),
|
|
||||||
'name correctly set');
|
|
||||||
frappe.lead_name = cur_frm.doc.name;
|
|
||||||
},
|
|
||||||
// create address and contact
|
|
||||||
() => frappe.click_link('Address & Contact'),
|
|
||||||
() => frappe.click_button('New Address'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.set_control('address_line1', 'Gateway'),
|
|
||||||
() => frappe.set_control('city', 'Mumbai'),
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(3),
|
|
||||||
() => assert.equal(frappe.get_route()[1], 'Lead',
|
|
||||||
'back to lead form'),
|
|
||||||
() => frappe.click_link('Address & Contact'),
|
|
||||||
() => assert.ok($('.address-box').text().includes('Mumbai'),
|
|
||||||
'city is seen in address box'),
|
|
||||||
|
|
||||||
// make opportunity
|
|
||||||
() => frappe.click_button('Make'),
|
|
||||||
() => frappe.click_link('Opportunity'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => assert.equal(cur_frm.doc.lead, frappe.lead_name,
|
|
||||||
'lead name correctly mapped'),
|
|
||||||
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,55 +0,0 @@
|
|||||||
QUnit.module("sales");
|
|
||||||
|
|
||||||
QUnit.test("test: lead", function (assert) {
|
|
||||||
assert.expect(5);
|
|
||||||
let done = assert.async();
|
|
||||||
let lead_name = frappe.utils.get_random(10);
|
|
||||||
frappe.run_serially([
|
|
||||||
// test lead creation
|
|
||||||
() => frappe.set_route("List", "Lead"),
|
|
||||||
() => frappe.new_doc("Lead"),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => cur_frm.set_value("company_name", lead_name),
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.lead_name.includes(lead_name),
|
|
||||||
'name correctly set');
|
|
||||||
frappe.lead_name = cur_frm.doc.name;
|
|
||||||
},
|
|
||||||
// create address and contact
|
|
||||||
() => frappe.click_link('Address & Contact'),
|
|
||||||
() => frappe.click_button('New Address'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.set_control('address_line1', 'Gateway'),
|
|
||||||
() => frappe.set_control('city', 'Mumbai'),
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(3),
|
|
||||||
() => assert.equal(frappe.get_route()[1], 'Lead',
|
|
||||||
'back to lead form'),
|
|
||||||
() => frappe.click_link('Address & Contact'),
|
|
||||||
() => assert.ok($('.address-box').text().includes('Mumbai'),
|
|
||||||
'city is seen in address box'),
|
|
||||||
|
|
||||||
() => frappe.click_button('New Contact'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.set_control('first_name', 'John'),
|
|
||||||
() => frappe.set_control('last_name', 'Doe'),
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(3),
|
|
||||||
() => frappe.set_route('Form', 'Lead', cur_frm.doc.links[0].link_name),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_link('Address & Contact'),
|
|
||||||
() => assert.ok($('.address-box').text().includes('John'),
|
|
||||||
'contact is seen in contact box'),
|
|
||||||
|
|
||||||
// make customer
|
|
||||||
() => frappe.click_button('Make'),
|
|
||||||
() => frappe.click_link('Customer'),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => assert.equal(cur_frm.doc.lead_name, frappe.lead_name,
|
|
||||||
'lead name correctly mapped'),
|
|
||||||
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -510,8 +510,7 @@
|
|||||||
"icon": "fa fa-info-sign",
|
"icon": "fa fa-info-sign",
|
||||||
"idx": 195,
|
"idx": 195,
|
||||||
"links": [],
|
"links": [],
|
||||||
"migration_hash": "d87c646ea2579b6900197fd41e6c5c5a",
|
"modified": "2021-10-21 12:04:30.151379",
|
||||||
"modified": "2021-10-21 11:04:30.151379",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Opportunity",
|
"name": "Opportunity",
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
QUnit.test("test: opportunity", function (assert) {
|
|
||||||
assert.expect(8);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => frappe.set_route('List', 'Opportunity'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button('New'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => cur_frm.set_value('opportunity_from', 'Customer'),
|
|
||||||
() => cur_frm.set_value('customer', 'Test Customer 1'),
|
|
||||||
|
|
||||||
// check items
|
|
||||||
() => cur_frm.set_value('with_items', 1),
|
|
||||||
() => frappe.tests.set_grid_values(cur_frm, 'items', [
|
|
||||||
[
|
|
||||||
{item_code:'Test Product 1'},
|
|
||||||
{qty: 4}
|
|
||||||
]
|
|
||||||
]),
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
assert.notOk(cur_frm.is_new(), 'saved');
|
|
||||||
frappe.opportunity_name = cur_frm.doc.name;
|
|
||||||
},
|
|
||||||
|
|
||||||
// close and re-open
|
|
||||||
() => frappe.click_button('Close'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => assert.equal(cur_frm.doc.status, 'Closed',
|
|
||||||
'closed'),
|
|
||||||
|
|
||||||
() => frappe.click_button('Reopen'),
|
|
||||||
() => assert.equal(cur_frm.doc.status, 'Open',
|
|
||||||
'reopened'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
|
|
||||||
// make quotation
|
|
||||||
() => frappe.click_button('Make'),
|
|
||||||
() => frappe.click_link('Quotation', 1),
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => {
|
|
||||||
assert.equal(frappe.get_route()[1], 'Quotation',
|
|
||||||
'made quotation');
|
|
||||||
assert.equal(cur_frm.doc.customer, 'Test Customer 1',
|
|
||||||
'customer set in quotation');
|
|
||||||
assert.equal(cur_frm.doc.items[0].item_code, 'Test Product 1',
|
|
||||||
'item set in quotation');
|
|
||||||
assert.equal(cur_frm.doc.items[0].qty, 4,
|
|
||||||
'qty set in quotation');
|
|
||||||
assert.equal(cur_frm.doc.items[0].prevdoc_docname, frappe.opportunity_name,
|
|
||||||
'opportunity set in quotation');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,24 +0,0 @@
|
|||||||
// Testing Setup Module in Education
|
|
||||||
QUnit.module('education');
|
|
||||||
|
|
||||||
QUnit.test('Test: Academic Term', function(assert){
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Academic Term', [
|
|
||||||
{academic_year: '2016-17'},
|
|
||||||
{term_name: "Semester 1"},
|
|
||||||
{term_start_date: '2016-07-20'},
|
|
||||||
{term_end_date:'2017-06-20'},
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.academic_year=='2016-17');
|
|
||||||
assert.ok(cur_frm.doc.term_name=='Semester 1');
|
|
||||||
assert.ok(cur_frm.doc.term_start_date=='2016-07-20');
|
|
||||||
assert.ok(cur_frm.doc.term_end_date=='2017-06-20');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,16 +0,0 @@
|
|||||||
// Education Assessment module
|
|
||||||
QUnit.module('education');
|
|
||||||
|
|
||||||
QUnit.test('Test: Assessment Criteria', function(assert){
|
|
||||||
assert.expect(0);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Assessment Criteria', [
|
|
||||||
{assessment_criteria: 'Pass'},
|
|
||||||
{assessment_criteria_group: 'Reservation'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,15 +0,0 @@
|
|||||||
// Education Assessment module
|
|
||||||
QUnit.module('education');
|
|
||||||
|
|
||||||
QUnit.test('Test: Assessment Criteria Group', function(assert){
|
|
||||||
assert.expect(0);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Assessment Criteria Group', [
|
|
||||||
{assessment_criteria_group: 'Reservation'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,65 +0,0 @@
|
|||||||
// Education Assessment module
|
|
||||||
QUnit.module('education');
|
|
||||||
|
|
||||||
QUnit.test('Test: Assessment Group', function(assert){
|
|
||||||
assert.expect(4);
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => frappe.set_route('Tree', 'Assessment Group'),
|
|
||||||
|
|
||||||
// Checking adding child without selecting any Node
|
|
||||||
() => frappe.tests.click_button('New'),
|
|
||||||
() => frappe.timeout(0.2),
|
|
||||||
() => {assert.equal($(`.msgprint`).text(), "Select a group node first.", "Error message success");},
|
|
||||||
() => frappe.tests.click_button('Close'),
|
|
||||||
() => frappe.timeout(0.2),
|
|
||||||
|
|
||||||
// Creating child nodes
|
|
||||||
() => frappe.tests.click_link('All Assessment Groups'),
|
|
||||||
() => frappe.map_group.make('Assessment-group-1'),
|
|
||||||
() => frappe.map_group.make('Assessment-group-4', "All Assessment Groups", 1),
|
|
||||||
() => frappe.tests.click_link('Assessment-group-4'),
|
|
||||||
() => frappe.map_group.make('Assessment-group-5', "Assessment-group-3", 0),
|
|
||||||
|
|
||||||
// Checking Edit button
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.click_link('Assessment-group-1'),
|
|
||||||
() => frappe.tests.click_button('Edit'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => {assert.deepEqual(frappe.get_route(), ["Form", "Assessment Group", "Assessment-group-1"], "Edit route checks");},
|
|
||||||
|
|
||||||
// Deleting child Node
|
|
||||||
() => frappe.set_route('Tree', 'Assessment Group'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.click_link('Assessment-group-1'),
|
|
||||||
() => frappe.tests.click_button('Delete'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
|
|
||||||
// Checking Collapse and Expand button
|
|
||||||
() => frappe.timeout(2),
|
|
||||||
() => frappe.tests.click_link('Assessment-group-4'),
|
|
||||||
() => frappe.click_button('Collapse'),
|
|
||||||
() => frappe.tests.click_link('All Assessment Groups'),
|
|
||||||
() => frappe.click_button('Collapse'),
|
|
||||||
() => {assert.ok($('.opened').size() == 0, 'Collapsed');},
|
|
||||||
() => frappe.click_button('Expand'),
|
|
||||||
() => {assert.ok($('.opened').size() > 0, 'Expanded');},
|
|
||||||
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
frappe.map_group = {
|
|
||||||
make:function(assessment_group_name, parent_assessment_group = 'All Assessment Groups', is_group = 0){
|
|
||||||
return frappe.run_serially([
|
|
||||||
() => frappe.click_button('Add Child'),
|
|
||||||
() => frappe.timeout(0.2),
|
|
||||||
() => cur_dialog.set_value('is_group', is_group),
|
|
||||||
() => cur_dialog.set_value('assessment_group_name', assessment_group_name),
|
|
||||||
() => cur_dialog.set_value('parent_assessment_group', parent_assessment_group),
|
|
||||||
() => frappe.click_button('Create New'),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,54 +0,0 @@
|
|||||||
// Testing Assessment Module in education
|
|
||||||
QUnit.module('education');
|
|
||||||
|
|
||||||
QUnit.test('Test: Assessment Plan', function(assert){
|
|
||||||
assert.expect(6);
|
|
||||||
let done = assert.async();
|
|
||||||
let room_name, instructor_name, assessment_name;
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => frappe.db.get_value('Room', {'room_name': 'Room 1'}, 'name'),
|
|
||||||
(room) => {room_name = room.message.name;}, // Fetching Room name
|
|
||||||
() => frappe.db.get_value('Instructor', {'instructor_name': 'Instructor 1'}, 'name'),
|
|
||||||
(instructor) => {instructor_name = instructor.message.name;}, // Fetching Instructor name
|
|
||||||
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Assessment Plan', [
|
|
||||||
{assessment_name: "Test-Mid-Term"},
|
|
||||||
{assessment_group: 'Assessment-group-5'},
|
|
||||||
{maximum_assessment_score: 100},
|
|
||||||
{student_group: 'test-course-wise-group-2'},
|
|
||||||
{course: 'Test_Sub'},
|
|
||||||
{grading_scale: 'GTU'},
|
|
||||||
{schedule_date: frappe.datetime.nowdate()},
|
|
||||||
{room: room_name},
|
|
||||||
{examiner: instructor_name},
|
|
||||||
{supervisor: instructor_name},
|
|
||||||
{from_time: "12:30:00"},
|
|
||||||
{to_time: "2:30:00"}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => {
|
|
||||||
assessment_name = cur_frm.doc.name; // Storing the name of current Assessment Plan
|
|
||||||
assert.equal(cur_frm.doc.assessment_criteria[0].assessment_criteria, 'Pass', 'Assessment Criteria auto-filled correctly');
|
|
||||||
assert.equal(cur_frm.doc.assessment_criteria[0].maximum_score, 100, 'Maximum score correctly set');
|
|
||||||
}, // Checking if the table was auto-filled upon selecting appropriate fields
|
|
||||||
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => {assert.equal(cur_frm.doc.docstatus, 1, "Assessment Plan submitted successfully");},
|
|
||||||
|
|
||||||
() => frappe.click_button('Assessment Result'), // Checking out Assessment Result button option
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => {
|
|
||||||
assert.deepEqual(frappe.get_route(), ["Form", "Assessment Result Tool"], 'Assessment Result properly linked');
|
|
||||||
assert.equal(cur_frm.doc.assessment_plan, assessment_name, 'Assessment correctly set');
|
|
||||||
assert.equal(cur_frm.doc.student_group, 'test-course-wise-group-2', 'Course for Assessment correctly set');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,73 +0,0 @@
|
|||||||
// Education Assessment module
|
|
||||||
QUnit.module('education');
|
|
||||||
|
|
||||||
QUnit.test('Test: Assessment Result', function(assert){
|
|
||||||
assert.expect(25);
|
|
||||||
let done = assert.async();
|
|
||||||
let student_list = [];
|
|
||||||
let assessment_name;
|
|
||||||
let tasks = []
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
// Saving Assessment Plan name
|
|
||||||
() => frappe.db.get_value('Assessment Plan', {'assessment_name': 'Test-Mid-Term'}, 'name'),
|
|
||||||
(assessment_plan) => {assessment_name = assessment_plan.message.name;},
|
|
||||||
// Fetching list of Student for which Result is supposed to be set
|
|
||||||
() => frappe.set_route('Form', 'Assessment Plan', assessment_name),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Assessment Result'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => cur_frm.refresh(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
$("tbody tr").each( function(i, input){
|
|
||||||
student_list.push($(input).data().student);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Looping through each student in the list and setting up their score
|
|
||||||
() => {
|
|
||||||
student_list.forEach(index => {
|
|
||||||
tasks.push(
|
|
||||||
() => frappe.set_route('List', 'Assessment Result', 'List'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.click_button('New'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => cur_frm.set_value('student', index),
|
|
||||||
() => cur_frm.set_value('assessment_plan', assessment_name),
|
|
||||||
() => frappe.timeout(0.2),
|
|
||||||
() => cur_frm.doc.details[0].score = (39 + (15 * student_list.indexOf(index))),
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
|
|
||||||
() => frappe.db.get_value('Assessment Plan', {'name': 'ASP00001'}, ['grading_scale', 'maximum_assessment_score']),
|
|
||||||
(assessment_plan) => {
|
|
||||||
assert.equal(cur_frm.doc.grading_scale, assessment_plan.message.grading_scale, 'Grading scale correctly fetched');
|
|
||||||
assert.equal(cur_frm.doc.maximum_score, assessment_plan.message.maximum_assessment_score, 'Maximum score correctly fetched');
|
|
||||||
|
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.education.api.get_grade",
|
|
||||||
args: {
|
|
||||||
"grading_scale": assessment_plan.message.grading_scale,
|
|
||||||
"percentage": cur_frm.doc.total_score
|
|
||||||
},
|
|
||||||
callback: function(r){
|
|
||||||
assert.equal(cur_frm.doc.grade, r.message, "Grade correctly calculated");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => {assert.equal();},
|
|
||||||
() => {assert.equal(cur_frm.doc.docstatus, 1, "Submitted successfully");},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return frappe.run_serially(tasks);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,29 +0,0 @@
|
|||||||
// Education Assessment module
|
|
||||||
QUnit.module('education');
|
|
||||||
|
|
||||||
QUnit.test('Test: Assessment Result Tool', function(assert){
|
|
||||||
assert.expect(1);
|
|
||||||
let done = assert.async();
|
|
||||||
let i, count = 0, assessment_name;
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
// Saving Assessment Plan name
|
|
||||||
() => frappe.db.get_value('Assessment Plan', {'assessment_name': 'Test-Mid-Term'}, 'name'),
|
|
||||||
(assessment_plan) => {assessment_name = assessment_plan.message.name;},
|
|
||||||
|
|
||||||
() => frappe.set_route('Form', 'Assessment Plan', assessment_name),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.tests.click_button('Assessment Result'),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => cur_frm.refresh(),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => {
|
|
||||||
for(i = 2; i < $('tbody tr').size() * 4; i = (i + 4)){
|
|
||||||
if(($(`tbody td:eq("${i}")`) != "") && ($(`tbody td:eq("${i+1}")`) != ""))
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
assert.equal($('tbody tr').size(), count, 'All grades correctly displayed');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,36 +0,0 @@
|
|||||||
// Testing Setup Module in education
|
|
||||||
QUnit.module('education');
|
|
||||||
|
|
||||||
QUnit.test('test course', function(assert) {
|
|
||||||
assert.expect(8);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Course', [
|
|
||||||
{course_name: 'Test_Subject'},
|
|
||||||
{course_code: 'Test_Sub'},
|
|
||||||
{department: 'Test Department'},
|
|
||||||
{course_abbreviation: 'Test_Sub'},
|
|
||||||
{course_intro: 'Test Subject Intro'},
|
|
||||||
{default_grading_scale: 'GTU'},
|
|
||||||
{assessment_criteria: [
|
|
||||||
[
|
|
||||||
{assessment_criteria: 'Pass'},
|
|
||||||
{weightage: 100}
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.course_name == 'Test_Subject', 'Course name correctly set');
|
|
||||||
assert.ok(cur_frm.doc.course_code == 'Test_Sub', 'Course code correctly set');
|
|
||||||
assert.ok(cur_frm.doc.department == 'Test Department', 'Department selected correctly');
|
|
||||||
assert.ok(cur_frm.doc.course_abbreviation == 'Test_Sub');
|
|
||||||
assert.ok(cur_frm.doc.course_intro == 'Test Subject Intro');
|
|
||||||
assert.ok(cur_frm.doc.default_grading_scale == 'GTU', 'Grading scale selected correctly');
|
|
||||||
assert.ok(cur_frm.doc.assessment_criteria[0].assessment_criteria == 'Pass', 'Assessment criteria selected correctly');
|
|
||||||
assert.ok(cur_frm.doc.assessment_criteria[0].weightage == '100');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,31 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// rename this file from _test_[name] to test_[name] to activate
|
|
||||||
// and remove above this line
|
|
||||||
|
|
||||||
// Testing Setup Module in Education
|
|
||||||
QUnit.module('education');
|
|
||||||
|
|
||||||
QUnit.test("test: Education Settings", function (assert) {
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
assert.expect(3);
|
|
||||||
|
|
||||||
frappe.run_serially([
|
|
||||||
() => frappe.set_route("List", "Education Settings"),
|
|
||||||
() => frappe.timeout(0.4),
|
|
||||||
() => {
|
|
||||||
return frappe.tests.set_form_values(cur_frm, [
|
|
||||||
{current_academic_year: '2016-17'},
|
|
||||||
{current_academic_term: '2016-17 (Semester 1)'},
|
|
||||||
{attendance_freeze_date: '2016-07-20'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
cur_frm.save();
|
|
||||||
assert.ok(cur_frm.doc.current_academic_year=="2016-17");
|
|
||||||
assert.ok(cur_frm.doc.current_academic_term=="2016-17 (Semester 1)");
|
|
||||||
assert.ok(cur_frm.doc.attendance_freeze_date=="2016-07-20");
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,31 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// rename this file from _test_[name] to test_[name] to activate
|
|
||||||
// and remove above this line
|
|
||||||
|
|
||||||
QUnit.test("test: Fees", function (assert) {
|
|
||||||
let done = assert.async();
|
|
||||||
|
|
||||||
// number of asserts
|
|
||||||
assert.expect(1);
|
|
||||||
|
|
||||||
frappe.run_serially('Fees', [
|
|
||||||
|
|
||||||
// insert a new Fees
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Fees', [
|
|
||||||
{student: 'STUD00001'},
|
|
||||||
{due_date: frappe.datetime.get_today()},
|
|
||||||
{fee_structure: 'FS00001'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
assert.equal(cur_frm.doc.grand_total===cur_frm.doc.outstanding_amount);
|
|
||||||
},
|
|
||||||
() => frappe.timeout(0.3),
|
|
||||||
() => cur_frm.save(),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
|
|
||||||
});
|
|
@ -1,102 +0,0 @@
|
|||||||
// Education Assessment module
|
|
||||||
QUnit.module('education');
|
|
||||||
|
|
||||||
QUnit.test('Test: Grading Scale', function(assert){
|
|
||||||
assert.expect(3);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Grading Scale', [
|
|
||||||
{grading_scale_name: 'GTU'},
|
|
||||||
{description: 'The score will be set according to 100 based system.'},
|
|
||||||
{intervals: [
|
|
||||||
[
|
|
||||||
{grade_code: 'AA'},
|
|
||||||
{threshold: '95'},
|
|
||||||
{grade_description: 'First Class + Distinction'}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{grade_code: 'AB'},
|
|
||||||
{threshold: '90'},
|
|
||||||
{grade_description: 'First Class'}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{grade_code: 'BB'},
|
|
||||||
{threshold: '80'},
|
|
||||||
{grade_description: 'Distinction'}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{grade_code: 'BC'},
|
|
||||||
{threshold: '70'},
|
|
||||||
{grade_description: 'Second Class'}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{grade_code: 'CC'},
|
|
||||||
{threshold: '60'},
|
|
||||||
{grade_description: 'Third Class'}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{grade_code: 'CD'},
|
|
||||||
{threshold: '50'},
|
|
||||||
{grade_description: 'Average'}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{grade_code: 'DD'},
|
|
||||||
{threshold: '40'},
|
|
||||||
{grade_description: 'Pass'}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{grade_code: 'FF'},
|
|
||||||
{threshold: '0'},
|
|
||||||
{grade_description: 'Fail'}
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Grading Scale', [
|
|
||||||
{grading_scale_name: 'GTU-2'},
|
|
||||||
{description: 'The score will be set according to 100 based system.'},
|
|
||||||
{intervals: [
|
|
||||||
[
|
|
||||||
{grade_code: 'AA'},
|
|
||||||
{threshold: '90'},
|
|
||||||
{grade_description: 'Distinction'}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{grade_code: 'FF'},
|
|
||||||
{threshold: '0'},
|
|
||||||
{grade_description: 'Fail'}
|
|
||||||
]
|
|
||||||
]}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => {
|
|
||||||
let grading_scale = ['GTU', 'GTU-2'];
|
|
||||||
let tasks = [];
|
|
||||||
grading_scale.forEach(index => {
|
|
||||||
tasks.push(
|
|
||||||
() => frappe.set_route('Form', 'Grading Scale', index),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.click_button('Submit'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => {assert.equal(cur_frm.doc.docstatus, 1, 'Submitted successfully');}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return frappe.run_serially(tasks);
|
|
||||||
},
|
|
||||||
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.set_route('Form', 'Grading Scale','GTU-2'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.click_button('Cancel'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => frappe.tests.click_button('Yes'),
|
|
||||||
() => frappe.timeout(0.5),
|
|
||||||
() => {assert.equal(cur_frm.doc.docstatus, 2, 'Cancelled successfully');},
|
|
||||||
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
@ -1,34 +0,0 @@
|
|||||||
// Testing Student Module in education
|
|
||||||
QUnit.module('education');
|
|
||||||
|
|
||||||
QUnit.test('Test: Guardian', function(assert){
|
|
||||||
assert.expect(9);
|
|
||||||
let done = assert.async();
|
|
||||||
frappe.run_serially([
|
|
||||||
() => {
|
|
||||||
return frappe.tests.make('Guardian', [
|
|
||||||
{guardian_name: 'Test Guardian'},
|
|
||||||
{email_address: 'guardian@testmail.com'},
|
|
||||||
{mobile_number: 9898980000},
|
|
||||||
{alternate_number: 8989890000},
|
|
||||||
{date_of_birth: '1982-07-22'},
|
|
||||||
{education: 'Testing'},
|
|
||||||
{occupation: 'Testing'},
|
|
||||||
{designation: 'Testing'},
|
|
||||||
{work_address: 'Testing address'}
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
assert.ok(cur_frm.doc.guardian_name == 'Test Guardian');
|
|
||||||
assert.ok(cur_frm.doc.email_address == 'guardian@testmail.com');
|
|
||||||
assert.ok(cur_frm.doc.mobile_number == 9898980000);
|
|
||||||
assert.ok(cur_frm.doc.alternate_number == 8989890000);
|
|
||||||
assert.ok(cur_frm.doc.date_of_birth == '1982-07-22');
|
|
||||||
assert.ok(cur_frm.doc.education == 'Testing');
|
|
||||||
assert.ok(cur_frm.doc.occupation == 'Testing');
|
|
||||||
assert.ok(cur_frm.doc.designation == 'Testing');
|
|
||||||
assert.ok(cur_frm.doc.work_address == 'Testing address');
|
|
||||||
},
|
|
||||||
() => done()
|
|
||||||
]);
|
|
||||||
});
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user