Merge branch 'develop' of https://github.com/frappe/erpnext into currency-exchange-settings

This commit is contained in:
Deepesh Garg 2022-01-10 17:32:15 +05:30
commit 3fdd47e71f
409 changed files with 3415 additions and 16978 deletions

View File

@ -24,20 +24,6 @@ body:
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:
@ -86,7 +72,7 @@ body:
- manual install
- FrappeCloud
validations:
required: true
required: false
- type: textarea
id: logs
@ -95,12 +81,7 @@ body:
description: Please copy and paste any relevant log output. This will be automatically formatted.
render: shell
- type: checkboxes
id: terms
- type: markdown
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
value: |
By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/frappe/erpnext/blob/develop/CODE_OF_CONDUCT.md)

View File

@ -12,17 +12,30 @@ git clone https://github.com/frappe/frappe --branch "${GITHUB_BASE_REF:-${GITHUB
bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench
mkdir ~/frappe-bench/sites/test_site
cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config.json" ~/frappe-bench/sites/test_site/
mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'"
mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
if [ "$DB" == "mariadb" ];then
cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config_mariadb.json" ~/frappe-bench/sites/test_site/site_config.json
else
cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config_postgres.json" ~/frappe-bench/sites/test_site/site_config.json
fi
mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE DATABASE test_frappe"
mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
mysql --host 127.0.0.1 --port 3306 -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'"
mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES"
if [ "$DB" == "mariadb" ];then
mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'"
mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE DATABASE test_frappe"
mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
mysql --host 127.0.0.1 --port 3306 -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'"
mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES"
fi
if [ "$DB" == "postgres" ];then
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE DATABASE test_frappe" -U postgres;
echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_frappe WITH PASSWORD 'test_frappe'" -U postgres;
fi
wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz
tar -xf /tmp/wkhtmltox.tar.xz -C /tmp

View File

@ -13,4 +13,4 @@
"host_name": "http://test_site:8000",
"install_apps": ["erpnext"],
"throttle_user_limit": 100
}
}

View File

@ -0,0 +1,18 @@
{
"db_host": "127.0.0.1",
"db_port": 5432,
"db_name": "test_frappe",
"db_password": "test_frappe",
"db_type": "postgres",
"allow_tests": true,
"auto_email_id": "test@example.com",
"mail_server": "smtp.example.com",
"mail_login": "test@example.com",
"mail_password": "test",
"admin_password": "admin",
"root_login": "postgres",
"root_password": "travis",
"host_name": "http://test_site:8000",
"install_apps": ["erpnext"],
"throttle_user_limit": 100
}

55
.github/labeler.yml vendored Normal file
View File

@ -0,0 +1,55 @@
accounts:
- 'erpnext/accounts/*'
- 'erpnext/controllers/accounts_controller.py'
- 'erpnext/controllers/taxes_and_totals.py'
stock:
- 'erpnext/stock/*'
- 'erpnext/controllers/stock_controller.py'
- 'erpnext/controllers/item_variant.py'
assets:
- 'erpnext/assets/*'
regional:
- 'erpnext/regional/*'
selling:
- 'erpnext/selling/*'
- 'erpnext/controllers/selling_controller.py'
buying:
- 'erpnext/buying/*'
- 'erpnext/controllers/buying_controller.py'
support:
- 'erpnext/support/*'
POS:
- 'pos*'
ecommerce:
- 'erpnext/e_commerce/*'
maintenance:
- 'erpnext/maintenance/*'
manufacturing:
- 'erpnext/manufacturing/*'
crm:
- 'erpnext/crm/*'
HR:
- 'erpnext/hr/*'
payroll:
- 'erpnext/payroll*'
projects:
- 'erpnext/projects/*'
# Any python files modifed but no test files modified
needs-tests:
- any: ['erpnext/**/*.py']
all: ['!erpnext/**/test*.py']

12
.github/workflows/labeller.yml vendored Normal file
View File

@ -0,0 +1,12 @@
name: "Pull Request Labeler"
on:
pull_request_target:
types: [opened, reopened]
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v3
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@ -80,6 +80,9 @@ jobs:
- name: Install
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env:
DB: mariadb
TYPE: server
- name: Run Patch Tests
run: |

View File

@ -1,10 +1,11 @@
name: Server
name: Server (Mariadb)
on:
pull_request:
paths-ignore:
- '**.js'
- '**.md'
- '**.html'
workflow_dispatch:
push:
branches: [ develop ]
@ -13,7 +14,7 @@ on:
- '**.md'
concurrency:
group: server-develop-${{ github.event.number }}
group: server-mariadb-develop-${{ github.event.number }}
cancel-in-progress: true
jobs:
@ -92,6 +93,7 @@ jobs:
- name: Install
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env:
DB: mariadb
TYPE: server
- name: Run Tests

View File

@ -0,0 +1,105 @@
name: Server (Postgres)
on:
pull_request:
paths-ignore:
- '**.js'
- '**.md'
- '**.html'
types: [opened, labelled, synchronize, reopened]
concurrency:
group: server-postgres-develop-${{ github.event.number }}
cancel-in-progress: true
jobs:
test:
if: ${{ contains(github.event.pull_request.labels.*.name, 'postgres') }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
container: [1, 2, 3]
name: Python Unit Tests
services:
postgres:
image: postgres:13.3
env:
POSTGRES_PASSWORD: travis
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Clone
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.7
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: 14
check-latest: true
- name: Add to Hosts
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env:
DB: postgres
TYPE: server
- name: Run Tests
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator
env:
TYPE: server
CI_BUILD_ID: ${{ github.run_id }}
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io

View File

@ -23,13 +23,13 @@ erpnext/stock/ @marination @rohitwaghchaure @ankush
erpnext/crm/ @ruchamahabal @pateljannat
erpnext/education/ @ruchamahabal @pateljannat
erpnext/healthcare/ @ruchamahabal @pateljannat @chillaranand
erpnext/hr/ @ruchamahabal @pateljannat
erpnext/non_profit/ @ruchamahabal
erpnext/payroll @ruchamahabal @pateljannat
erpnext/projects/ @ruchamahabal @pateljannat
erpnext/controllers @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination
erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination @ankush
erpnext/patches/ @deepeshgarg007 @nextchamp-saqib @marination @ankush
erpnext/public/ @nextchamp-saqib @marination
.github/ @surajshetty3416 @ankush
.github/ @ankush
requirements.txt @gavindsouza

1
dev-requirements.txt Normal file
View File

@ -0,0 +1 @@
hypothesis~=6.31.0

View File

@ -55,9 +55,9 @@ def set_perpetual_inventory(enable=1, company=None):
company.enable_perpetual_inventory = enable
company.save()
def encode_company_abbr(name, company):
def encode_company_abbr(name, company=None, abbr=None):
'''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)
if parts[-1].lower() != company_abbr.lower():

View File

@ -43,12 +43,12 @@ frappe.ui.form.on('Account', {
frm.trigger('add_toolbar_buttons');
}
if (frm.has_perm('write')) {
frm.add_custom_button(__('Update Account Name / Number'), function () {
frm.trigger("update_account_number");
});
frm.add_custom_button(__('Merge Account'), function () {
frm.trigger("merge_account");
});
}, __('Actions'));
frm.add_custom_button(__('Update Account Name / Number'), function () {
frm.trigger("update_account_number");
}, __('Actions'));
}
}
},
@ -59,11 +59,12 @@ frappe.ui.form.on('Account', {
}
},
add_toolbar_buttons: function(frm) {
frm.add_custom_button(__('Chart of Accounts'),
function () { frappe.set_route("Tree", "Account"); });
frm.add_custom_button(__('Chart of Accounts'), () => {
frappe.set_route("Tree", "Account");
}, __('View'));
if (frm.doc.is_group == 1) {
frm.add_custom_button(__('Group to Non-Group'), function () {
frm.add_custom_button(__('Convert to Non-Group'), function () {
return frappe.call({
doc: frm.doc,
method: 'convert_group_to_ledger',
@ -71,10 +72,11 @@ frappe.ui.form.on('Account', {
frm.refresh();
}
});
});
}, __('Actions'));
} else if (cint(frm.doc.is_group) == 0
&& frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
frm.add_custom_button(__('Ledger'), function () {
frm.add_custom_button(__('General Ledger'), function () {
frappe.route_options = {
"account": frm.doc.name,
"from_date": frappe.sys_defaults.year_start_date,
@ -82,9 +84,9 @@ frappe.ui.form.on('Account', {
"company": frm.doc.company
};
frappe.set_route("query-report", "General Ledger");
});
}, __('View'));
frm.add_custom_button(__('Non-Group to Group'), function () {
frm.add_custom_button(__('Convert to Group'), function () {
return frappe.call({
doc: frm.doc,
method: 'convert_ledger_to_group',
@ -92,7 +94,7 @@ frappe.ui.form.on('Account', {
frm.refresh();
}
});
});
}, __('Actions'));
}
},

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -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'),
]);
}
};

View File

@ -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);
}
};

View File

@ -7,7 +7,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
frm.set_query("bank_account", function () {
return {
filters: {
company: ["in", frm.doc.company],
company: frm.doc.company,
'is_company_account': 1
},
};

View File

@ -218,6 +218,8 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
# updated clear date of all the vouchers based on the bank transaction
vouchers = json.loads(vouchers)
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
company_account = frappe.db.get_value('Bank Account', transaction.bank_account, 'account')
if transaction.unallocated_amount == 0:
frappe.throw(_("This bank transaction is already fully reconciled"))
total_amount = 0
@ -226,7 +228,7 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
total_amount += get_paid_amount(frappe._dict({
'payment_document': voucher['payment_doctype'],
'payment_entry': voucher['payment_name'],
}), transaction.currency)
}), transaction.currency, company_account)
if total_amount > transaction.unallocated_amount:
frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction"))
@ -261,7 +263,7 @@ def get_linked_payments(bank_transaction_name, document_types = None):
return matching
def check_matching(bank_account, company, transaction, document_types):
# combine all types of vocuhers
# combine all types of vouchers
subquery = get_queries(bank_account, company, transaction, document_types)
filters = {
"amount": transaction.unallocated_amount,
@ -343,13 +345,11 @@ def get_pe_matching_query(amount_condition, account_from_to, transaction):
def get_je_matching_query(amount_condition, transaction):
# get matching journal entry query
# We have mapping at the bank level
# So one bank could have both types of bank accounts like asset and liability
# So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type
company_account = frappe.get_value("Bank Account", transaction.bank_account, "account")
root_type = frappe.get_value("Account", company_account, "root_type")
if root_type == "Liability":
cr_or_dr = "debit" if transaction.withdrawal > 0 else "credit"
else:
cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
return f"""

View File

@ -239,7 +239,8 @@ frappe.ui.form.on("Bank Statement Import", {
"withdrawal",
"description",
"reference_number",
"bank_account"
"bank_account",
"currency"
],
},
});

View File

@ -102,7 +102,7 @@ def get_total_allocated_amount(payment_entry):
AND
bt.docstatus = 1""", (payment_entry.payment_document, payment_entry.payment_entry), as_dict=True)
def get_paid_amount(payment_entry, currency):
def get_paid_amount(payment_entry, currency, bank_account):
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
paid_amount_field = "paid_amount"
@ -115,7 +115,7 @@ def get_paid_amount(payment_entry, currency):
payment_entry.payment_entry, paid_amount_field)
elif payment_entry.payment_document == "Journal Entry":
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_credit")
return frappe.db.get_value('Journal Entry Account', {'parent': payment_entry.payment_entry, 'account': bank_account}, "sum(credit_in_account_currency)")
elif payment_entry.payment_document == "Expense Claim":
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed")

View File

@ -31,7 +31,7 @@ frappe.ui.form.on("Journal Entry", {
if(frm.doc.docstatus==1) {
frm.add_custom_button(__('Reverse Journal Entry'), function() {
return erpnext.journal_entry.reverse_journal_entry(frm);
}, __('Make'));
}, __('Actions'));
}
if (frm.doc.__islocal) {

View File

@ -13,6 +13,7 @@
"voucher_type",
"naming_series",
"finance_book",
"reversal_of",
"tax_withholding_category",
"column_break1",
"from_template",
@ -515,13 +516,21 @@
"fieldname": "apply_tds",
"fieldtype": "Check",
"label": "Apply Tax Withholding Amount "
},
{
"depends_on": "eval:doc.docstatus",
"fieldname": "reversal_of",
"fieldtype": "Link",
"label": "Reversal Of",
"options": "Journal Entry",
"read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 176,
"is_submittable": 1,
"links": [],
"modified": "2021-09-09 15:31:14.484029",
"modified": "2022-01-04 13:39:36.485954",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",

View File

@ -1157,9 +1157,8 @@ def make_inter_company_journal_entry(name, voucher_type, company):
def make_reverse_journal_entry(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
def update_accounts(source, target, source_parent):
target.reference_type = "Journal Entry"
target.reference_name = source_parent.name
def post_process(source, target):
target.reversal_of = source.name
doclist = get_mapped_doc("Journal Entry", source_name, {
"Journal Entry": {
@ -1177,9 +1176,8 @@ def make_reverse_journal_entry(source_name, target_doc=None):
"debit": "credit",
"credit_in_account_currency": "debit_in_account_currency",
"credit": "debit",
},
"postprocess": update_accounts,
}
},
}, target_doc)
}, target_doc, post_process)
return doclist

View File

@ -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()
]);
});

View File

@ -0,0 +1,128 @@
// Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Ledger Merge', {
setup: function(frm) {
frappe.realtime.on('ledger_merge_refresh', ({ ledger_merge }) => {
if (ledger_merge !== frm.doc.name) return;
frappe.model.clear_doc(frm.doc.doctype, frm.doc.name);
frappe.model.with_doc(frm.doc.doctype, frm.doc.name).then(() => {
frm.refresh();
});
});
frappe.realtime.on('ledger_merge_progress', data => {
if (data.ledger_merge !== frm.doc.name) return;
let message = __('Merging {0} of {1}', [data.current, data.total]);
let percent = Math.floor((data.current * 100) / data.total);
frm.dashboard.show_progress(__('Merge Progress'), percent, message);
frm.page.set_indicator(__('In Progress'), 'orange');
});
frm.set_query("account", function(doc) {
if (!doc.company) frappe.throw(__('Please set Company'));
if (!doc.root_type) frappe.throw(__('Please set Root Type'));
return {
filters: {
root_type: doc.root_type,
company: doc.company
}
};
});
frm.set_query('account', 'merge_accounts', function(doc) {
if (!doc.company) frappe.throw(__('Please set Company'));
if (!doc.root_type) frappe.throw(__('Please set Root Type'));
if (!doc.account) frappe.throw(__('Please set Account'));
let acc = [doc.account];
frm.doc.merge_accounts.forEach((row) => {
acc.push(row.account);
});
return {
filters: {
is_group: doc.is_group,
root_type: doc.root_type,
name: ["not in", acc],
company: doc.company
}
};
});
},
refresh: function(frm) {
frm.page.hide_icon_group();
frm.trigger('set_merge_status');
frm.trigger('update_primary_action');
},
after_save: function(frm) {
setTimeout(() => {
frm.trigger('update_primary_action');
}, 500);
},
update_primary_action: function(frm) {
if (frm.is_dirty()) {
frm.enable_save();
return;
}
frm.disable_save();
if (frm.doc.status !== 'Success') {
if (!frm.is_new()) {
let label = frm.doc.status === 'Pending' ? __('Start Merge') : __('Retry');
frm.page.set_primary_action(label, () => frm.events.start_merge(frm));
} else {
frm.page.set_primary_action(__('Save'), () => frm.save());
}
}
},
start_merge: function(frm) {
frm.call({
method: 'form_start_merge',
args: { docname: frm.doc.name },
btn: frm.page.btn_primary
}).then(r => {
if (r.message === true) {
frm.disable_save();
}
});
},
set_merge_status: function(frm) {
if (frm.doc.status == "Pending") return;
let successful_records = 0;
frm.doc.merge_accounts.forEach((row) => {
if (row.merged) successful_records += 1;
});
let message_args = [successful_records, frm.doc.merge_accounts.length];
frm.dashboard.set_headline(__('Successfully merged {0} out of {1}.', message_args));
},
root_type: function(frm) {
frm.set_value('account', '');
frm.set_value('merge_accounts', []);
},
company: function(frm) {
frm.set_value('account', '');
frm.set_value('merge_accounts', []);
}
});
frappe.ui.form.on('Ledger Merge Accounts', {
merge_accounts_add: function(frm) {
frm.trigger('update_primary_action');
},
merge_accounts_remove: function(frm) {
frm.trigger('update_primary_action');
},
account: function(frm, cdt, cdn) {
let row = frappe.get_doc(cdt, cdn);
row.account_name = row.account;
frm.refresh_field('merge_accounts');
frm.trigger('update_primary_action');
}
});

View File

@ -0,0 +1,130 @@
{
"actions": [],
"autoname": "format:{account_name} merger on {creation}",
"creation": "2021-12-09 15:38:04.556584",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"section_break_1",
"root_type",
"account",
"account_name",
"column_break_3",
"company",
"status",
"is_group",
"section_break_5",
"merge_accounts"
],
"fields": [
{
"depends_on": "root_type",
"fieldname": "account",
"fieldtype": "Link",
"label": "Account",
"options": "Account",
"reqd": 1,
"set_only_once": 1
},
{
"fieldname": "section_break_1",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"fieldname": "merge_accounts",
"fieldtype": "Table",
"label": "Accounts to Merge",
"options": "Ledger Merge Accounts",
"reqd": 1
},
{
"depends_on": "account",
"fieldname": "section_break_5",
"fieldtype": "Section Break"
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"reqd": 1,
"set_only_once": 1
},
{
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Status",
"options": "Pending\nSuccess\nPartial Success\nError",
"read_only": 1
},
{
"fieldname": "root_type",
"fieldtype": "Select",
"label": "Root Type",
"options": "\nAsset\nLiability\nIncome\nExpense\nEquity",
"reqd": 1,
"set_only_once": 1
},
{
"depends_on": "account",
"fetch_from": "account.account_name",
"fetch_if_empty": 1,
"fieldname": "account_name",
"fieldtype": "Data",
"label": "Account Name",
"read_only": 1,
"reqd": 1
},
{
"default": "0",
"depends_on": "account",
"fetch_from": "account.is_group",
"fieldname": "is_group",
"fieldtype": "Check",
"label": "Is Group",
"read_only": 1
}
],
"hide_toolbar": 1,
"links": [],
"modified": "2021-12-12 21:34:55.155146",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Ledger Merge",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -0,0 +1,76 @@
# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.model.document import Document
from erpnext.accounts.doctype.account.account import merge_account
class LedgerMerge(Document):
def start_merge(self):
from frappe.core.page.background_jobs.background_jobs import get_info
from frappe.utils.background_jobs import enqueue
from frappe.utils.scheduler import is_scheduler_inactive
if is_scheduler_inactive() and not frappe.flags.in_test:
frappe.throw(
_("Scheduler is inactive. Cannot merge accounts."), title=_("Scheduler Inactive")
)
enqueued_jobs = [d.get("job_name") for d in get_info()]
if self.name not in enqueued_jobs:
enqueue(
start_merge,
queue="default",
timeout=6000,
event="ledger_merge",
job_name=self.name,
docname=self.name,
now=frappe.conf.developer_mode or frappe.flags.in_test,
)
return True
return False
@frappe.whitelist()
def form_start_merge(docname):
return frappe.get_doc("Ledger Merge", docname).start_merge()
def start_merge(docname):
ledger_merge = frappe.get_doc("Ledger Merge", docname)
successful_merges = 0
total = len(ledger_merge.merge_accounts)
for row in ledger_merge.merge_accounts:
if not row.merged:
try:
merge_account(
row.account,
ledger_merge.account,
ledger_merge.is_group,
ledger_merge.root_type,
ledger_merge.company
)
row.db_set('merged', 1)
frappe.db.commit()
successful_merges += 1
frappe.publish_realtime("ledger_merge_progress", {
"ledger_merge": ledger_merge.name,
"current": successful_merges,
"total": total
}
)
except Exception:
frappe.db.rollback()
frappe.log_error(title=ledger_merge.name)
finally:
if successful_merges == total:
ledger_merge.db_set('status', 'Success')
elif successful_merges > 0:
ledger_merge.db_set('status', 'Partial Success')
else:
ledger_merge.db_set('status', 'Error')
frappe.publish_realtime("ledger_merge_refresh", {"ledger_merge": ledger_merge.name})

View File

@ -0,0 +1,118 @@
# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
import frappe
from erpnext.accounts.doctype.ledger_merge.ledger_merge import start_merge
class TestLedgerMerge(unittest.TestCase):
def test_merge_success(self):
if not frappe.db.exists("Account", "Indirect Expenses - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Indirect Expenses"
acc.is_group = 1
acc.parent_account = "Expenses - _TC"
acc.company = "_Test Company"
acc.insert()
if not frappe.db.exists("Account", "Indirect Test Expenses - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Indirect Test Expenses"
acc.is_group = 1
acc.parent_account = "Expenses - _TC"
acc.company = "_Test Company"
acc.insert()
if not frappe.db.exists("Account", "Administrative Test Expenses - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Administrative Test Expenses"
acc.parent_account = "Indirect Test Expenses - _TC"
acc.company = "_Test Company"
acc.insert()
doc = frappe.get_doc({
"doctype": "Ledger Merge",
"company": "_Test Company",
"root_type": frappe.db.get_value("Account", "Indirect Test Expenses - _TC", "root_type"),
"account": "Indirect Expenses - _TC",
"merge_accounts": [
{
"account": "Indirect Test Expenses - _TC",
"account_name": "Indirect Expenses"
}
]
}).insert(ignore_permissions=True)
parent = frappe.db.get_value("Account", "Administrative Test Expenses - _TC", "parent_account")
self.assertEqual(parent, "Indirect Test Expenses - _TC")
start_merge(doc.name)
parent = frappe.db.get_value("Account", "Administrative Test Expenses - _TC", "parent_account")
self.assertEqual(parent, "Indirect Expenses - _TC")
self.assertFalse(frappe.db.exists("Account", "Indirect Test Expenses - _TC"))
def test_partial_merge_success(self):
if not frappe.db.exists("Account", "Indirect Income - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Indirect Income"
acc.is_group = 1
acc.parent_account = "Income - _TC"
acc.company = "_Test Company"
acc.insert()
if not frappe.db.exists("Account", "Indirect Test Income - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Indirect Test Income"
acc.is_group = 1
acc.parent_account = "Income - _TC"
acc.company = "_Test Company"
acc.insert()
if not frappe.db.exists("Account", "Administrative Test Income - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Administrative Test Income"
acc.parent_account = "Indirect Test Income - _TC"
acc.company = "_Test Company"
acc.insert()
doc = frappe.get_doc({
"doctype": "Ledger Merge",
"company": "_Test Company",
"root_type": frappe.db.get_value("Account", "Indirect Income - _TC", "root_type"),
"account": "Indirect Income - _TC",
"merge_accounts": [
{
"account": "Indirect Test Income - _TC",
"account_name": "Indirect Test Income"
},
{
"account": "Administrative Test Income - _TC",
"account_name": "Administrative Test Income"
}
]
}).insert(ignore_permissions=True)
parent = frappe.db.get_value("Account", "Administrative Test Income - _TC", "parent_account")
self.assertEqual(parent, "Indirect Test Income - _TC")
start_merge(doc.name)
parent = frappe.db.get_value("Account", "Administrative Test Income - _TC", "parent_account")
self.assertEqual(parent, "Indirect Income - _TC")
self.assertFalse(frappe.db.exists("Account", "Indirect Test Income - _TC"))
self.assertTrue(frappe.db.exists("Account", "Administrative Test Income - _TC"))
def tearDown(self):
for entry in frappe.db.get_all("Ledger Merge"):
frappe.delete_doc("Ledger Merge", entry.name)
test_accounts = [
"Indirect Test Expenses - _TC",
"Administrative Test Expenses - _TC",
"Indirect Test Income - _TC",
"Administrative Test Income - _TC"
]
for account in test_accounts:
frappe.delete_doc_if_exists("Account", account)

View File

@ -0,0 +1,52 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2021-12-09 15:44:58.033398",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"account",
"account_name",
"merged"
],
"fields": [
{
"columns": 4,
"fieldname": "account",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Account",
"options": "Account",
"reqd": 1
},
{
"columns": 2,
"default": "0",
"fieldname": "merged",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Merged",
"read_only": 1
},
{
"columns": 4,
"fieldname": "account_name",
"fieldtype": "Data",
"label": "Account Name",
"read_only": 1,
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-12-10 15:27:24.477139",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Ledger Merge Accounts",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -0,0 +1,9 @@
# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class LedgerMergeAccounts(Document):
pass

View File

@ -75,7 +75,7 @@
],
"hide_toolbar": 1,
"issingle": 1,
"modified": "2019-07-25 14:57:33.187689",
"modified": "2022-01-04 15:25:06.053187",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Opening Invoice Creation Tool",

View File

@ -148,7 +148,7 @@ def make_company():
company.company_name = "_Test Opening Invoice Company"
company.abbr = "_TOIC"
company.default_currency = "INR"
company.country = "India"
company.country = "Pakistan"
company.insert()
return company

View File

@ -2,7 +2,7 @@
# For license information, please see license.txt
import frappe
from frappe import _
from frappe import _, bold
from frappe.model.document import Document
@ -12,6 +12,17 @@ class PartyLink(Document):
frappe.throw(_("Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."),
title=_("Invalid Primary Role"))
existing_party_link = frappe.get_all('Party Link', {
'primary_party': self.primary_party,
'secondary_party': self.secondary_party
}, pluck="primary_role")
if existing_party_link:
frappe.throw(_('{} {} is already linked with {} {}')
.format(
self.primary_role, bold(self.primary_party),
self.secondary_role, bold(self.secondary_party)
))
existing_party_link = frappe.get_all('Party Link', {
'primary_party': self.secondary_party
}, pluck="primary_role")

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -15,6 +15,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
update_multi_mode_option,
)
from erpnext.accounts.party import get_due_date, get_party_account
from erpnext.stock.doctype.batch.batch import get_batch_qty, get_pos_reserved_batch_qty
from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos, get_serial_nos
@ -124,9 +125,26 @@ class POSInvoice(SalesInvoice):
frappe.throw(_("Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no.")
.format(item.idx, bold_invalid_serial_nos), title=_("Item Unavailable"))
elif invalid_serial_nos:
frappe.throw(_("Row #{}: Serial Nos. {} has already been transacted into another POS Invoice. Please select valid serial no.")
frappe.throw(_("Row #{}: Serial Nos. {} have already been transacted into another POS Invoice. Please select valid serial no.")
.format(item.idx, bold_invalid_serial_nos), title=_("Item Unavailable"))
def validate_pos_reserved_batch_qty(self, item):
filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no":item.batch_no}
available_batch_qty = get_batch_qty(item.batch_no, item.warehouse, item.item_code)
reserved_batch_qty = get_pos_reserved_batch_qty(filters)
bold_item_name = frappe.bold(item.item_name)
bold_extra_batch_qty_needed = frappe.bold(abs(available_batch_qty - reserved_batch_qty - item.qty))
bold_invalid_batch_no = frappe.bold(item.batch_no)
if (available_batch_qty - reserved_batch_qty) == 0:
frappe.throw(_("Row #{}: Batch No. {} of item {} has no stock available. Please select valid batch no.")
.format(item.idx, bold_invalid_batch_no, bold_item_name), title=_("Item Unavailable"))
elif (available_batch_qty - reserved_batch_qty - item.qty) < 0:
frappe.throw(_("Row #{}: Batch No. {} of item {} has less than required stock available, {} more required")
.format(item.idx, bold_invalid_batch_no, bold_item_name, bold_extra_batch_qty_needed), title=_("Item Unavailable"))
def validate_delivered_serial_nos(self, item):
serial_nos = get_serial_nos(item.serial_no)
delivered_serial_nos = frappe.db.get_list('Serial No', {
@ -149,6 +167,8 @@ class POSInvoice(SalesInvoice):
if d.serial_no:
self.validate_pos_reserved_serial_nos(d)
self.validate_delivered_serial_nos(d)
elif d.batch_no:
self.validate_pos_reserved_batch_qty(d)
else:
if allow_negative_stock:
return

View File

@ -521,6 +521,41 @@ class TestPOSInvoice(unittest.TestCase):
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
self.assertEqual(rounded_total, 400)
def test_pos_batch_item_qty_validation(self):
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_batch_item_with_batch,
)
create_batch_item_with_batch('_BATCH ITEM', 'TestBatch 01')
item = frappe.get_doc('Item', '_BATCH ITEM')
batch = frappe.get_doc('Batch', 'TestBatch 01')
batch.submit()
item.batch_no = 'TestBatch 01'
item.save()
se = make_stock_entry(target="_Test Warehouse - _TC", item_code="_BATCH ITEM", qty=2, basic_rate=100, batch_no='TestBatch 01')
pos_inv1 = create_pos_invoice(item=item.name, rate=300, qty=1, do_not_submit=1)
pos_inv1.items[0].batch_no = 'TestBatch 01'
pos_inv1.save()
pos_inv1.submit()
pos_inv2 = create_pos_invoice(item=item.name, rate=300, qty=2, do_not_submit=1)
pos_inv2.items[0].batch_no = 'TestBatch 01'
pos_inv2.save()
self.assertRaises(frappe.ValidationError, pos_inv2.submit)
#teardown
pos_inv1.reload()
pos_inv1.cancel()
pos_inv1.delete()
pos_inv2.reload()
pos_inv2.delete()
se.cancel()
batch.reload()
batch.cancel()
batch.delete()
def create_pos_invoice(**args):
args = frappe._dict(args)
pos_profile = None
@ -557,7 +592,8 @@ def create_pos_invoice(**args):
"income_account": args.income_account or "Sales - _TC",
"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
"cost_center": args.cost_center or "_Test Cost Center - _TC",
"serial_no": args.serial_no
"serial_no": args.serial_no,
"batch_no": args.batch_no
})
if not args.do_not_save:
@ -570,3 +606,8 @@ def create_pos_invoice(**args):
pos_inv.payment_schedule = []
return pos_inv
def make_batch_item(item_name):
from erpnext.stock.doctype.item.test_item import make_item
if not frappe.db.exists(item_name):
return make_item(item_name, dict(has_batch_no = 1, create_new_batch = 1, is_stock_item=1))

View File

@ -166,7 +166,7 @@ class TestPricingRule(unittest.TestCase):
"item_group": "Products",
},
{
"item_group": "Seed",
"item_group": "_Test Item Group",
},
],
"selling": 1,

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -505,11 +505,11 @@ class PurchaseInvoice(BuyingController):
# Checked both rounding_adjustment and rounded_total
# because rounded_total had value even before introcution of posting GLE based on rounded total
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
base_grand_total = flt(self.base_rounded_total if (self.base_rounding_adjustment and self.base_rounded_total)
else self.base_grand_total, self.precision("base_grand_total"))
if grand_total and not self.is_internal_transfer():
# Did not use base_grand_total to book rounding loss gle
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
self.precision("grand_total"))
gl_entries.append(
self.get_gl_dict({
"account": self.credit_to,
@ -517,8 +517,8 @@ class PurchaseInvoice(BuyingController):
"party": self.supplier,
"due_date": self.due_date,
"against": self.against_expense_account,
"credit": grand_total_in_company_currency,
"credit_in_account_currency": grand_total_in_company_currency \
"credit": base_grand_total,
"credit_in_account_currency": base_grand_total \
if self.party_account_currency==self.company_currency else grand_total,
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype,

View File

@ -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()
]);
});

View File

@ -986,7 +986,7 @@ class TestPurchaseInvoice(unittest.TestCase):
pi = make_purchase_invoice(item=item.name, qty=1, rate=100, do_not_save=True)
pi.set_posting_time = 1
pi.posting_date = '2019-03-15'
pi.posting_date = '2019-01-10'
pi.items[0].enable_deferred_expense = 1
pi.items[0].service_start_date = "2019-01-10"
pi.items[0].service_end_date = "2019-03-15"
@ -1236,7 +1236,7 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
def update_tax_witholding_category(company, account):
from erpnext.accounts.utils import get_fiscal_year
fiscal_year = get_fiscal_year(fiscal_year='2021')
fiscal_year = get_fiscal_year(date=nowdate())
if not frappe.db.get_value('Tax Withholding Rate',
{'parent': 'TDS - 194 - Dividends - Individual', 'from_date': ('>=', fiscal_year[1]),

View File

@ -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()
]);
});

View File

@ -862,11 +862,11 @@ class SalesInvoice(SellingController):
# Checked both rounding_adjustment and rounded_total
# because rounded_total had value even before introcution of posting GLE based on rounded total
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
base_grand_total = flt(self.base_rounded_total if (self.base_rounding_adjustment and self.base_rounded_total)
else self.base_grand_total, self.precision("base_grand_total"))
if grand_total and not self.is_internal_transfer():
# Didnot use base_grand_total to book rounding loss gle
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
self.precision("grand_total"))
gl_entries.append(
self.get_gl_dict({
"account": self.debit_to,
@ -874,8 +874,8 @@ class SalesInvoice(SellingController):
"party": self.customer,
"due_date": self.due_date,
"against": self.against_income_account,
"debit": grand_total_in_company_currency,
"debit_in_account_currency": grand_total_in_company_currency \
"debit": base_grand_total,
"debit_in_account_currency": base_grand_total \
if self.party_account_currency==self.company_currency else grand_total,
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype,
@ -1049,6 +1049,8 @@ class SalesInvoice(SellingController):
frappe.flags.is_reverse_depr_entry = False
asset.flags.ignore_validate_update_after_submit = True
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()
def get_posting_date_of_sales_invoice(self):
@ -1071,6 +1073,12 @@ class SalesInvoice(SellingController):
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
def enable_discount_accounting(self):
if not hasattr(self, "_enable_discount_accounting"):

View File

@ -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()
]);
});

View File

@ -20,6 +20,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_comp
from erpnext.accounts.utils import PaymentEntryUnlinkError
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries
from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data
from erpnext.controllers.accounts_controller import update_invoice_status
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
from erpnext.regional.india.utils import get_ewb_data
@ -2385,6 +2386,41 @@ class TestSalesInvoice(unittest.TestCase):
si.reload()
self.assertEqual(si.status, "Paid")
def test_update_invoice_status(self):
today = nowdate()
# Sales Invoice without Payment Schedule
si = create_sales_invoice(posting_date=add_days(today, -5))
# Sales Invoice with Payment Schedule
si_with_payment_schedule = create_sales_invoice(do_not_submit=True)
si_with_payment_schedule.extend("payment_schedule", [
{
"due_date": add_days(today, -5),
"invoice_portion": 50,
"payment_amount": si_with_payment_schedule.grand_total / 2
},
{
"due_date": add_days(today, 5),
"invoice_portion": 50,
"payment_amount": si_with_payment_schedule.grand_total / 2
}
])
si_with_payment_schedule.submit()
for invoice in (si, si_with_payment_schedule):
invoice.db_set("status", "Unpaid")
update_invoice_status()
invoice.reload()
self.assertEqual(invoice.status, "Overdue")
invoice.db_set("status", "Unpaid and Discounted")
update_invoice_status()
invoice.reload()
self.assertEqual(invoice.status, "Overdue and Discounted")
def test_sales_commission(self):
si = frappe.copy_doc(test_records[0])
item = copy.deepcopy(si.get('items')[0])

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -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()
]);
});

View File

@ -117,6 +117,11 @@ frappe.query_reports["Accounts Receivable Summary"] = {
"label": __("Show Future Payments"),
"fieldtype": "Check",
},
{
"fieldname":"show_gl_balance",
"label": __("Show GL Balance"),
"fieldtype": "Check",
},
],
onload: function(report) {

View File

@ -4,7 +4,8 @@
import frappe
from frappe import _, scrub
from frappe.utils import cint
from frappe.utils import cint, flt
from six import iteritems
from erpnext.accounts.party import get_partywise_advanced_payment_amount
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
@ -36,7 +37,10 @@ class AccountsReceivableSummary(ReceivablePayableReport):
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {}
for party, party_dict in self.party_total.items():
if self.filters.show_gl_balance:
gl_balance_map = get_gl_balance(self.filters.report_date)
for party, party_dict in iteritems(self.party_total):
if party_dict.outstanding == 0:
continue
@ -55,6 +59,10 @@ class AccountsReceivableSummary(ReceivablePayableReport):
# but in summary report advance shown in separate column
row.paid -= row.advance
if self.filters.show_gl_balance:
row.gl_balance = gl_balance_map.get(party)
row.diff = flt(row.outstanding) - flt(row.gl_balance)
self.data.append(row)
def get_party_total(self, args):
@ -114,6 +122,10 @@ class AccountsReceivableSummary(ReceivablePayableReport):
self.add_column(_(credit_debit_label), fieldname='credit_note')
self.add_column(_('Outstanding Amount'), fieldname='outstanding')
if self.filters.show_gl_balance:
self.add_column(_('GL Balance'), fieldname='gl_balance')
self.add_column(_('Difference'), fieldname='diff')
self.setup_ageing_columns()
if self.party_type == "Customer":
@ -140,3 +152,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
# Add column for total due amount
self.add_column(label="Total Amount Due", fieldname='total_due')
def get_gl_balance(report_date):
return frappe._dict(frappe.db.get_all("GL Entry", fields=['party', 'sum(debit - credit)'],
filters={'posting_date': ("<=", report_date), 'is_cancelled': 0}, group_by='party', as_list=1))

View File

@ -370,7 +370,7 @@ def get_account_heads(root_type, companies, filters):
accounts = get_accounts(root_type, filters)
if not accounts:
return None, None
return None, None, None
accounts = update_parent_account_names(accounts)

View File

@ -1,8 +0,0 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Agriculture Analysis Criteria', {
refresh: function(frm) {
}
});

View File

@ -1,182 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:title",
"beta": 0,
"creation": "2017-12-05 16:37:46.599982",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "title",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Title",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "standard",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Standard",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "linked_doctype",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Linked Doctype",
"length": 0,
"no_copy": 0,
"options": "\nWater Analysis\nSoil Analysis\nPlant Analysis\nFertilizer\nSoil Texture\nWeather",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-11-04 03:27:36.678832",
"modified_by": "Administrator",
"module": "Agriculture",
"name": "Agriculture Analysis Criteria",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Agriculture Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Agriculture User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Agriculture",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class AgricultureAnalysisCriteria(Document):
pass

View File

@ -1,8 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
class TestAgricultureAnalysisCriteria(unittest.TestCase):
pass

View File

@ -1,8 +0,0 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Agriculture Task', {
refresh: function(frm) {
}
});

View File

@ -1,212 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "AG-TASK-.#####",
"beta": 0,
"creation": "2017-10-26 15:51:19.602452",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "task_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Task Name",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "start_day",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Start Day",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "end_day",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "End Day",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Ignore holidays",
"fieldname": "holiday_management",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Holiday Management",
"length": 0,
"no_copy": 0,
"options": "Ignore holidays\nPrevious Business Day\nNext Business Day",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Low",
"fieldname": "priority",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Priority",
"length": 0,
"no_copy": 0,
"options": "Low\nMedium\nHigh\nUrgent",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-11-04 03:28:08.679157",
"modified_by": "Administrator",
"module": "Agriculture",
"name": "Agriculture Task",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Agriculture",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class AgricultureTask(Document):
pass

View File

@ -1,8 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
class TestAgricultureTask(unittest.TestCase):
pass

View File

@ -1,55 +0,0 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.provide("erpnext.crop");
frappe.ui.form.on('Crop', {
refresh: (frm) => {
frm.fields_dict.materials_required.grid.set_column_disp('bom_no', false);
}
});
frappe.ui.form.on("BOM Item", {
item_code: (frm, cdt, cdn) => {
erpnext.crop.update_item_rate_uom(frm, cdt, cdn);
},
qty: (frm, cdt, cdn) => {
erpnext.crop.update_item_qty_amount(frm, cdt, cdn);
},
rate: (frm, cdt, cdn) => {
erpnext.crop.update_item_qty_amount(frm, cdt, cdn);
}
});
erpnext.crop.update_item_rate_uom = function(frm, cdt, cdn) {
let material_list = ['materials_required', 'produce', 'byproducts'];
material_list.forEach((material) => {
frm.doc[material].forEach((item, index) => {
if (item.name == cdn && item.item_code){
frappe.call({
method:'erpnext.agriculture.doctype.crop.crop.get_item_details',
args: {
item_code: item.item_code
},
callback: (r) => {
frappe.model.set_value('BOM Item', item.name, 'uom', r.message.uom);
frappe.model.set_value('BOM Item', item.name, 'rate', r.message.rate);
}
});
}
});
});
};
erpnext.crop.update_item_qty_amount = function(frm, cdt, cdn) {
let material_list = ['materials_required', 'produce', 'byproducts'];
material_list.forEach((material) => {
frm.doc[material].forEach((item, index) => {
if (item.name == cdn){
if (!frappe.model.get_value('BOM Item', item.name, 'qty'))
frappe.model.set_value('BOM Item', item.name, 'qty', 1);
frappe.model.set_value('BOM Item', item.name, 'amount', item.qty * item.rate);
}
});
});
};

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.model.document import Document
class Crop(Document):
def validate(self):
self.validate_crop_tasks()
def validate_crop_tasks(self):
for task in self.agriculture_task:
if task.start_day > task.end_day:
frappe.throw(_("Start day is greater than end day in task '{0}'").format(task.task_name))
# Verify that the crop period is correct
max_crop_period = max([task.end_day for task in self.agriculture_task])
self.period = max(self.period, max_crop_period)
# Sort the crop tasks based on start days,
# maintaining the order for same-day tasks
self.agriculture_task.sort(key=lambda task: task.start_day)
@frappe.whitelist()
def get_item_details(item_code):
item = frappe.get_doc('Item', item_code)
return {"uom": item.stock_uom, "rate": item.valuation_rate}

View File

@ -1,12 +0,0 @@
from frappe import _
def get_data():
return {
'transactions': [
{
'label': _('Crop Cycle'),
'items': ['Crop Cycle']
}
]
}

View File

@ -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()
]);
});

View File

@ -1,13 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
import frappe
test_dependencies = ["Fertilizer"]
class TestCrop(unittest.TestCase):
def test_crop_period(self):
basil = frappe.get_doc('Crop', 'Basil from seed')
self.assertEqual(basil.period, 15)

View File

@ -1,80 +0,0 @@
[
{
"doctype": "Item",
"item_code": "Basil Seeds",
"item_name": "Basil Seeds",
"item_group": "Seed"
},
{
"doctype": "Item",
"item_code": "Twigs",
"item_name": "Twigs",
"item_group": "By-product"
},
{
"doctype": "Item",
"item_code": "Basil Leaves",
"item_name": "Basil Leaves",
"item_group": "Produce"
},
{
"doctype": "Crop",
"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"
}]
}
]

View File

@ -1,48 +0,0 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Crop Cycle', {
refresh: (frm) => {
if (!frm.doc.__islocal)
frm.add_custom_button(__('Reload Linked Analysis'), () => frm.call("reload_linked_analysis"));
frappe.realtime.on("List of Linked Docs", (output) => {
let analysis_doctypes = ['Soil Texture', 'Plant Analysis', 'Soil Analysis'];
let analysis_doctypes_docs = ['soil_texture', 'plant_analysis', 'soil_analysis'];
let obj_to_append = {soil_analysis: [], soil_texture: [], plant_analysis: []};
output['Location'].forEach( (land_doc) => {
analysis_doctypes.forEach( (doctype) => {
output[doctype].forEach( (analysis_doc) => {
let point_to_be_tested = JSON.parse(analysis_doc.location).features[0].geometry.coordinates;
let poly_of_land = JSON.parse(land_doc.location).features[0].geometry.coordinates[0];
if (is_in_land_unit(point_to_be_tested, poly_of_land)){
obj_to_append[analysis_doctypes_docs[analysis_doctypes.indexOf(doctype)]].push(analysis_doc.name);
}
});
});
});
frm.call('append_to_child', {
obj_to_append: obj_to_append
});
});
}
});
function is_in_land_unit(point, vs) {
// ray-casting algorithm based on
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
var x = point[0], y = point[1];
var inside = false;
for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
var xi = vs[i][0], yi = vs[i][1];
var xj = vs[j][0], yj = vs[j][1];
var intersect = ((yi > y) != (yj > y))
&& (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
return inside;
};

View File

@ -1,904 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:title",
"beta": 0,
"creation": "2017-11-02 03:09:35.449880",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "title",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Title",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "crop",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Crop",
"length": 0,
"no_copy": 0,
"options": "Crop",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Linked Location",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "A link to all the Locations in which the Crop is growing",
"fieldname": "linked_location",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Linked Location",
"length": 0,
"no_copy": 0,
"options": "Linked Location",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_3",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.__islocal",
"fieldname": "project",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Project",
"length": 0,
"no_copy": 0,
"options": "Project",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_12",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "This will be day 1 of the crop cycle",
"fieldname": "start_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Start Date",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "project.expected_end_date",
"fieldname": "end_date",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "End Date",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_7",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "iso_8601_standard",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "ISO 8601 standard",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "cycle_type",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Cycle Type",
"length": 0,
"no_copy": 0,
"options": "Yearly\nLess than a year",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_12",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "The minimum length between each plant in the field for optimum growth",
"fetch_from": "crop.crop_spacing",
"fieldname": "crop_spacing",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Crop Spacing",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "crop_spacing_uom",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Crop Spacing UOM",
"length": 0,
"no_copy": 0,
"options": "UOM",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "The minimum distance between rows of plants for optimum growth",
"fetch_from": "crop.row_spacing",
"fieldname": "row_spacing",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Row Spacing",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "row_spacing_uom",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Row Spacing UOM",
"length": 0,
"no_copy": 0,
"options": "UOM",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.__islocal",
"description": "List of diseases detected on the field. When selected it'll automatically add a list of tasks to deal with the disease ",
"fieldname": "section_break_14",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Detected Diseases",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "detected_disease",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Detected Disease",
"length": 0,
"no_copy": 0,
"options": "Detected Disease",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:false",
"columns": 0,
"fieldname": "section_break_22",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "LInked Analysis",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "soil_texture",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Soil Texture",
"length": 0,
"no_copy": 0,
"options": "Linked Soil Texture",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "soil_analysis",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Soil Analysis",
"length": 0,
"no_copy": 0,
"options": "Linked Soil Analysis",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "plant_analysis",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Plant Analysis",
"length": 0,
"no_copy": 0,
"options": "Linked Plant Analysis",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-11-04 03:31:47.602312",
"modified_by": "Administrator",
"module": "Agriculture",
"name": "Crop Cycle",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Agriculture Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Agriculture User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Agriculture",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -1,126 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import ast
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import add_days
class CropCycle(Document):
def validate(self):
self.set_missing_values()
def after_insert(self):
self.create_crop_cycle_project()
self.create_tasks_for_diseases()
def on_update(self):
self.create_tasks_for_diseases()
def set_missing_values(self):
crop = frappe.get_doc('Crop', self.crop)
if not self.crop_spacing_uom:
self.crop_spacing_uom = crop.crop_spacing_uom
if not self.row_spacing_uom:
self.row_spacing_uom = crop.row_spacing_uom
def create_crop_cycle_project(self):
crop = frappe.get_doc('Crop', self.crop)
self.project = self.create_project(crop.period, crop.agriculture_task)
self.create_task(crop.agriculture_task, self.project, self.start_date)
def create_tasks_for_diseases(self):
for disease in self.detected_disease:
if not disease.tasks_created:
self.import_disease_tasks(disease.disease, disease.start_date)
disease.tasks_created = True
frappe.msgprint(_("Tasks have been created for managing the {0} disease (on row {1})").format(disease.disease, disease.idx))
def import_disease_tasks(self, disease, start_date):
disease_doc = frappe.get_doc('Disease', disease)
self.create_task(disease_doc.treatment_task, self.project, start_date)
def create_project(self, period, crop_tasks):
project = frappe.get_doc({
"doctype": "Project",
"project_name": self.title,
"expected_start_date": self.start_date,
"expected_end_date": add_days(self.start_date, period - 1)
}).insert()
return project.name
def create_task(self, crop_tasks, project_name, start_date):
for crop_task in crop_tasks:
frappe.get_doc({
"doctype": "Task",
"subject": crop_task.get("task_name"),
"priority": crop_task.get("priority"),
"project": project_name,
"exp_start_date": add_days(start_date, crop_task.get("start_day") - 1),
"exp_end_date": add_days(start_date, crop_task.get("end_day") - 1)
}).insert()
@frappe.whitelist()
def reload_linked_analysis(self):
linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis']
required_fields = ['location', 'name', 'collection_datetime']
output = {}
for doctype in linked_doctypes:
output[doctype] = frappe.get_all(doctype, fields=required_fields)
output['Location'] = []
for location in self.linked_location:
output['Location'].append(frappe.get_doc('Location', location.location))
frappe.publish_realtime("List of Linked Docs",
output, user=frappe.session.user)
@frappe.whitelist()
def append_to_child(self, obj_to_append):
for doctype in obj_to_append:
for doc_name in set(obj_to_append[doctype]):
self.append(doctype, {doctype: doc_name})
self.save()
def get_coordinates(doc):
return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('coordinates')
def get_geometry_type(doc):
return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('type')
def is_in_location(point, vs):
x, y = point
inside = False
j = len(vs) - 1
i = 0
while i < len(vs):
xi, yi = vs[i]
xj, yj = vs[j]
intersect = ((yi > y) != (yj > y)) and (
x < (xj - xi) * (y - yi) / (yj - yi) + xi)
if intersect:
inside = not inside
i = j
j += 1
return inside

View File

@ -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()
]);
});

View File

@ -1,72 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
import frappe
from frappe.utils import datetime
test_dependencies = ["Crop", "Fertilizer", "Location", "Disease"]
class TestCropCycle(unittest.TestCase):
def test_crop_cycle_creation(self):
cycle = frappe.get_doc('Crop Cycle', 'Basil from seed 2017')
self.assertTrue(frappe.db.exists('Crop Cycle', 'Basil from seed 2017'))
# check if the tasks were created
self.assertEqual(check_task_creation(), True)
self.assertEqual(check_project_creation(), True)
def check_task_creation():
all_task_dict = {
"Survey and find the aphid locations": {
"exp_start_date": datetime.date(2017, 11, 21),
"exp_end_date": datetime.date(2017, 11, 22)
},
"Apply Pesticides": {
"exp_start_date": datetime.date(2017, 11, 23),
"exp_end_date": datetime.date(2017, 11, 23)
},
"Plough the field": {
"exp_start_date": datetime.date(2017, 11, 11),
"exp_end_date": datetime.date(2017, 11, 11)
},
"Plant the seeds": {
"exp_start_date": datetime.date(2017, 11, 12),
"exp_end_date": datetime.date(2017, 11, 13)
},
"Water the field": {
"exp_start_date": datetime.date(2017, 11, 14),
"exp_end_date": datetime.date(2017, 11, 14)
},
"First harvest": {
"exp_start_date": datetime.date(2017, 11, 18),
"exp_end_date": datetime.date(2017, 11, 18)
},
"Add the fertilizer": {
"exp_start_date": datetime.date(2017, 11, 20),
"exp_end_date": datetime.date(2017, 11, 22)
},
"Final cut": {
"exp_start_date": datetime.date(2017, 11, 25),
"exp_end_date": datetime.date(2017, 11, 25)
}
}
all_tasks = frappe.get_all('Task')
for task in all_tasks:
sample_task = frappe.get_doc('Task', task.name)
if sample_task.subject in list(all_task_dict):
if sample_task.exp_start_date != all_task_dict[sample_task.subject]['exp_start_date'] or sample_task.exp_end_date != all_task_dict[sample_task.subject]['exp_end_date']:
return False
all_task_dict.pop(sample_task.subject)
return True if not all_task_dict else False
def check_project_creation():
return True if frappe.db.exists('Project', {'project_name': 'Basil from seed 2017'}) else False

View File

@ -1,15 +0,0 @@
[
{
"doctype": "Crop Cycle",
"title": "Basil from seed 2017",
"linked_location": [{
"location": "Basil Farm"
}],
"crop": "Basil from seed",
"start_date": "2017-11-11",
"detected_disease": [{
"disease": "Aphids",
"start_date": "2017-11-21"
}]
}
]

View File

@ -1,142 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-11-20 17:31:30.772779",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disease",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Disease",
"length": 0,
"no_copy": 0,
"options": "Disease",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "start_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Start Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "tasks_created",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Tasks Created",
"length": 0,
"no_copy": 1,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-11-04 03:27:47.463994",
"modified_by": "Administrator",
"module": "Agriculture",
"name": "Detected Disease",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Agriculture",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -1,9 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from frappe.model.document import Document
class DetectedDisease(Document):
pass

View File

@ -1,8 +0,0 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Disease', {
refresh: function(frm) {
}
});

View File

@ -1,308 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:common_name",
"beta": 0,
"creation": "2017-11-20 17:16:54.496355",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "common_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Common Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "scientific_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Scientific Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_3",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "treatment_task",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Treatment Task",
"length": 0,
"no_copy": 0,
"options": "Agriculture Task",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "treatment_period",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Treatment Period",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Treatment Task",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Long Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-11-04 03:27:25.076490",
"modified_by": "Administrator",
"module": "Agriculture",
"name": "Disease",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Agriculture Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Agriculture User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Agriculture",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -1,19 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.model.document import Document
class Disease(Document):
def validate(self):
max_period = 0
for task in self.treatment_task:
# validate start_day is not > end_day
if task.start_day > task.end_day:
frappe.throw(_("Start day is greater than end day in task '{0}'").format(task.task_name))
# to calculate the period of the Crop Cycle
if task.end_day > max_period: max_period = task.end_day
self.treatment_period = max_period

View File

@ -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()
]);
});

View File

@ -1,12 +0,0 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
import frappe
class TestDisease(unittest.TestCase):
def test_treatment_period(self):
disease = frappe.get_doc('Disease', 'Aphids')
self.assertEqual(disease.treatment_period, 3)

View File

@ -1,18 +0,0 @@
[
{
"doctype": "Disease",
"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"
}]
}
]

Some files were not shown because too many files have changed in this diff Show More