Merge branch 'develop' into remove-nonprofit

This commit is contained in:
ChillarAnand 2022-01-24 11:35:47 +05:30
commit 7086d3f3d7
318 changed files with 3195 additions and 17751 deletions

View File

@ -40,6 +40,7 @@ body:
- HR - HR
- projects - projects
- support - support
- CRM
- assets - assets
- integrations - integrations
- quality - quality
@ -48,6 +49,7 @@ body:
- agriculture - agriculture
- education - education
- non-profit - non-profit
- other
validations: validations:
required: true required: true

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']

View File

@ -12,7 +12,7 @@ jobs:
- name: 'Setup Environment' - name: 'Setup Environment'
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 3.6 python-version: 3.8
- name: 'Clone repo' - name: 'Clone repo'
uses: actions/checkout@v2 uses: actions/checkout@v2

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

@ -34,7 +34,7 @@ jobs:
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 3.7 python-version: 3.8
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v2 uses: actions/setup-node@v2

View File

@ -46,7 +46,7 @@ jobs:
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 3.7 python-version: 3.8
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v2 uses: actions/setup-node@v2

View File

@ -46,7 +46,7 @@ jobs:
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 3.7 python-version: 3.8
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v2 uses: actions/setup-node@v2

View File

@ -36,7 +36,7 @@ jobs:
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 3.7 python-version: 3.8
- uses: actions/setup-node@v2 - uses: actions/setup-node@v2
with: with:

View File

@ -23,13 +23,13 @@ erpnext/stock/ @marination @rohitwaghchaure @ankush
erpnext/crm/ @ruchamahabal @pateljannat erpnext/crm/ @ruchamahabal @pateljannat
erpnext/education/ @ruchamahabal @pateljannat erpnext/education/ @ruchamahabal @pateljannat
erpnext/healthcare/ @ruchamahabal @pateljannat @chillaranand
erpnext/hr/ @ruchamahabal @pateljannat erpnext/hr/ @ruchamahabal @pateljannat
erpnext/non_profit/ @ruchamahabal
erpnext/payroll @ruchamahabal @pateljannat erpnext/payroll @ruchamahabal @pateljannat
erpnext/projects/ @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 requirements.txt @gavindsouza

View File

@ -254,11 +254,13 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
enable_check = "enable_deferred_revenue" \ enable_check = "enable_deferred_revenue" \
if doc.doctype=="Sales Invoice" else "enable_deferred_expense" if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
accounts_frozen_upto = frappe.get_cached_value('Accounts Settings', 'None', 'acc_frozen_upto')
def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on): def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on):
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date) start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
if not (start_date and end_date): return if not (start_date and end_date): return
account_currency = get_account_currency(item.expense_account) account_currency = get_account_currency(item.expense_account or item.income_account)
if doc.doctype == "Sales Invoice": if doc.doctype == "Sales Invoice":
against, project = doc.customer, doc.project against, project = doc.customer, doc.project
credit_account, debit_account = item.income_account, item.deferred_revenue_account credit_account, debit_account = item.income_account, item.deferred_revenue_account
@ -279,6 +281,10 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
if not amount: if not amount:
return return
# check if books nor frozen till endate:
if getdate(end_date) >= getdate(accounts_frozen_upto):
end_date = get_last_day(add_days(accounts_frozen_upto, 1))
if via_journal_entry: if via_journal_entry:
book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount, book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry) base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
@ -406,8 +412,6 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
'account': credit_account, 'account': credit_account,
'credit': base_amount, 'credit': base_amount,
'credit_in_account_currency': amount, 'credit_in_account_currency': amount,
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
'party': against,
'account_currency': account_currency, 'account_currency': account_currency,
'reference_name': doc.name, 'reference_name': doc.name,
'reference_type': doc.doctype, 'reference_type': doc.doctype,
@ -420,8 +424,6 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
'account': debit_account, 'account': debit_account,
'debit': base_amount, 'debit': base_amount,
'debit_in_account_currency': amount, 'debit_in_account_currency': amount,
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
'party': against,
'account_currency': account_currency, 'account_currency': account_currency,
'reference_name': doc.name, 'reference_name': doc.name,
'reference_type': doc.doctype, 'reference_type': doc.doctype,

View File

@ -16,6 +16,7 @@ from frappe.utils.xlsxutils import ILLEGAL_CHARACTERS_RE, handle_html
from openpyxl.styles import Font from openpyxl.styles import Font
from openpyxl.utils import get_column_letter from openpyxl.utils import get_column_letter
INVALID_VALUES = ("", None)
class BankStatementImport(DataImport): class BankStatementImport(DataImport):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -95,6 +96,18 @@ def download_errored_template(data_import_name):
data_import = frappe.get_doc("Bank Statement Import", data_import_name) data_import = frappe.get_doc("Bank Statement Import", data_import_name)
data_import.export_errored_rows() data_import.export_errored_rows()
def parse_data_from_template(raw_data):
data = []
for i, row in enumerate(raw_data):
if all(v in INVALID_VALUES for v in row):
# empty row
continue
data.append(row)
return data
def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options): def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options):
"""This method runs in background job""" """This method runs in background job"""
@ -104,7 +117,8 @@ def start_import(data_import, bank_account, import_file_path, google_sheets_url,
file = import_file_path if import_file_path else google_sheets_url file = import_file_path if import_file_path else google_sheets_url
import_file = ImportFile("Bank Transaction", file = file, import_type="Insert New Records") import_file = ImportFile("Bank Transaction", file = file, import_type="Insert New Records")
data = import_file.raw_data
data = parse_data_from_template(import_file.raw_data)
if import_file_path: if import_file_path:
add_bank_account(data, bank_account) add_bank_account(data, bank_account)

View File

@ -0,0 +1,45 @@
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Currency Exchange Settings', {
service_provider: function(frm) {
if (frm.doc.service_provider == "exchangerate.host") {
let result = ['result'];
let params = {
date: '{transaction_date}',
from: '{from_currency}',
to: '{to_currency}'
};
add_param(frm, "https://api.exchangerate.host/convert", params, result);
} else if (frm.doc.service_provider == "frankfurter.app") {
let result = ['rates', '{to_currency}'];
let params = {
base: '{from_currency}',
symbols: '{to_currency}'
};
add_param(frm, "https://frankfurter.app/{transaction_date}", params, result);
}
}
});
function add_param(frm, api, params, result) {
var row;
frm.clear_table("req_params");
frm.clear_table("result_key");
frm.doc.api_endpoint = api;
$.each(params, function(key, value) {
row = frm.add_child("req_params");
row.key = key;
row.value = value;
});
$.each(result, function(key, value) {
row = frm.add_child("result_key");
row.key = value;
});
frm.refresh_fields();
}

View File

@ -0,0 +1,126 @@
{
"actions": [],
"creation": "2022-01-10 13:03:26.237081",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"api_details_section",
"service_provider",
"api_endpoint",
"url",
"column_break_3",
"help",
"section_break_2",
"req_params",
"column_break_4",
"result_key"
],
"fields": [
{
"fieldname": "api_details_section",
"fieldtype": "Section Break",
"label": "API Details"
},
{
"fieldname": "api_endpoint",
"fieldtype": "Data",
"in_list_view": 1,
"label": "API Endpoint",
"read_only_depends_on": "eval: doc.service_provider != \"Custom\"",
"reqd": 1
},
{
"fieldname": "url",
"fieldtype": "Data",
"label": "Example URL",
"read_only": 1
},
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"fieldname": "help",
"fieldtype": "HTML",
"label": "Help",
"options": "<h3>Currency Exchange Settings Help</h3>\n<p>There are 3 variables that could be used within the endpoint, result key and in values of the parameter.</p>\n<p>Exchange rate between {from_currency} and {to_currency} on {transaction_date} is fetched by the API.</p>\n<p>Example: If your endpoint is exchange.com/2021-08-01, then, you will have to input exchange.com/{transaction_date}</p>"
},
{
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"label": "Request Parameters"
},
{
"fieldname": "req_params",
"fieldtype": "Table",
"label": "Parameters",
"options": "Currency Exchange Settings Details",
"read_only_depends_on": "eval: doc.service_provider != \"Custom\"",
"reqd": 1
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"fieldname": "result_key",
"fieldtype": "Table",
"label": "Result Key",
"options": "Currency Exchange Settings Result",
"read_only_depends_on": "eval: doc.service_provider != \"Custom\"",
"reqd": 1
},
{
"fieldname": "service_provider",
"fieldtype": "Select",
"label": "Service Provider",
"options": "frankfurter.app\nexchangerate.host\nCustom",
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2022-01-10 15:51:14.521174",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Currency Exchange Settings",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"role": "Accounts Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"role": "Accounts User",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@ -0,0 +1,82 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
import requests
from frappe import _
from frappe.model.document import Document
from frappe.utils import nowdate
class CurrencyExchangeSettings(Document):
def validate(self):
self.set_parameters_and_result()
response, value = self.validate_parameters()
self.validate_result(response, value)
def set_parameters_and_result(self):
if self.service_provider == 'exchangerate.host':
self.set('result_key', [])
self.set('req_params', [])
self.api_endpoint = "https://api.exchangerate.host/convert"
self.append('result_key', {'key': 'result'})
self.append('req_params', {'key': 'date', 'value': '{transaction_date}'})
self.append('req_params', {'key': 'from', 'value': '{from_currency}'})
self.append('req_params', {'key': 'to', 'value': '{to_currency}'})
elif self.service_provider == 'frankfurter.app':
self.set('result_key', [])
self.set('req_params', [])
self.api_endpoint = "https://frankfurter.app/{transaction_date}"
self.append('result_key', {'key': 'rates'})
self.append('result_key', {'key': '{to_currency}'})
self.append('req_params', {'key': 'base', 'value': '{from_currency}'})
self.append('req_params', {'key': 'symbols', 'value': '{to_currency}'})
def validate_parameters(self):
if frappe.flags.in_test:
return None, None
params = {}
for row in self.req_params:
params[row.key] = row.value.format(
transaction_date=nowdate(),
to_currency='INR',
from_currency='USD'
)
api_url = self.api_endpoint.format(
transaction_date=nowdate(),
to_currency='INR',
from_currency='USD'
)
try:
response = requests.get(api_url, params=params)
except requests.exceptions.RequestException as e:
frappe.throw("Error: " + str(e))
response.raise_for_status()
value = response.json()
return response, value
def validate_result(self, response, value):
if frappe.flags.in_test:
return
try:
for key in self.result_key:
value = value[str(key.key).format(
transaction_date=nowdate(),
to_currency='INR',
from_currency='USD'
)]
except Exception:
frappe.throw("Invalid result key. Response: " + response.text)
if not isinstance(value, (int, float)):
frappe.throw(_("Returned exchange rate is neither integer not float."))
self.url = response.url
frappe.msgprint("Exchange rate of USD to INR is " + str(value))

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
import unittest
class TestCurrencyExchangeSettings(unittest.TestCase):
pass

View File

@ -0,0 +1,39 @@
{
"actions": [],
"creation": "2021-09-02 14:54:49.033512",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"key",
"value"
],
"fields": [
{
"fieldname": "key",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Key",
"reqd": 1
},
{
"fieldname": "value",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Value",
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-11-03 19:14:55.889037",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Currency Exchange Settings Details",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

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 CurrencyExchangeSettingsDetails(Document):
pass

View File

@ -0,0 +1,31 @@
{
"actions": [],
"creation": "2021-09-03 13:17:22.088259",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"key"
],
"fields": [
{
"fieldname": "key",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Key",
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-11-03 19:14:40.054245",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Currency Exchange Settings Result",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

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 CurrencyExchangeSettingsResult(Document):
pass

View File

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

View File

@ -13,6 +13,7 @@
"voucher_type", "voucher_type",
"naming_series", "naming_series",
"finance_book", "finance_book",
"reversal_of",
"tax_withholding_category", "tax_withholding_category",
"column_break1", "column_break1",
"from_template", "from_template",
@ -515,13 +516,21 @@
"fieldname": "apply_tds", "fieldname": "apply_tds",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Apply Tax Withholding Amount " "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", "icon": "fa fa-file-text",
"idx": 176, "idx": 176,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-09-09 15:31:14.484029", "modified": "2022-01-04 13:39:36.485954",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry", "name": "Journal Entry",

View File

@ -407,13 +407,14 @@ class JournalEntry(AccountsController):
debit_or_credit = 'Debit' if d.debit else 'Credit' debit_or_credit = 'Debit' if d.debit else 'Credit'
party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no, party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no,
debit_or_credit) debit_or_credit)
against_voucher = ['', against_voucher[1]]
else: else:
if d.reference_type == "Sales Invoice": if d.reference_type == "Sales Invoice":
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1] party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
else: else:
party_account = against_voucher[1] party_account = against_voucher[1]
if (against_voucher[0] != d.party or party_account != d.account): if (against_voucher[0] != cstr(d.party) or party_account != d.account):
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}") frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1], .format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
d.reference_type, d.reference_name)) d.reference_type, d.reference_name))
@ -478,13 +479,22 @@ class JournalEntry(AccountsController):
def set_against_account(self): def set_against_account(self):
accounts_debited, accounts_credited = [], [] accounts_debited, accounts_credited = [], []
for d in self.get("accounts"): if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
if flt(d.debit > 0): accounts_debited.append(d.party or d.account) for d in self.get('accounts'):
if flt(d.credit) > 0: accounts_credited.append(d.party or d.account) if d.reference_type == 'Sales Invoice':
field = 'customer'
else:
field = 'supplier'
for d in self.get("accounts"): d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited))) else:
if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited))) for d in self.get("accounts"):
if flt(d.debit > 0): accounts_debited.append(d.party or d.account)
if flt(d.credit) > 0: accounts_credited.append(d.party or d.account)
for d in self.get("accounts"):
if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited)))
if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited)))
def validate_debit_credit_amount(self): def validate_debit_credit_amount(self):
for d in self.get('accounts'): for d in self.get('accounts'):
@ -1157,9 +1167,8 @@ def make_inter_company_journal_entry(name, voucher_type, company):
def make_reverse_journal_entry(source_name, target_doc=None): def make_reverse_journal_entry(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
def update_accounts(source, target, source_parent): def post_process(source, target):
target.reference_type = "Journal Entry" target.reversal_of = source.name
target.reference_name = source_parent.name
doclist = get_mapped_doc("Journal Entry", source_name, { doclist = get_mapped_doc("Journal Entry", source_name, {
"Journal Entry": { "Journal Entry": {
@ -1177,9 +1186,8 @@ def make_reverse_journal_entry(source_name, target_doc=None):
"debit": "credit", "debit": "credit",
"credit_in_account_currency": "debit_in_account_currency", "credit_in_account_currency": "debit_in_account_currency",
"credit": "debit", "credit": "debit",
}, }
"postprocess": update_accounts,
}, },
}, target_doc) }, target_doc, post_process)
return doclist return doclist

View File

@ -2,7 +2,7 @@
# For license information, please see license.txt # For license information, please see license.txt
import frappe import frappe
from frappe import _ from frappe import _, bold
from frappe.model.document import Document 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."), frappe.throw(_("Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."),
title=_("Invalid Primary Role")) 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', { existing_party_link = frappe.get_all('Party Link', {
'primary_party': self.secondary_party 'primary_party': self.secondary_party
}, pluck="primary_role") }, pluck="primary_role")

View File

@ -3,6 +3,7 @@
import json import json
from functools import reduce
import frappe import frappe
from frappe import ValidationError, _, scrub, throw from frappe import ValidationError, _, scrub, throw
@ -1523,6 +1524,10 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
pe.received_amount = received_amount pe.received_amount = received_amount
pe.letter_head = doc.get("letter_head") pe.letter_head = doc.get("letter_head")
if dt in ['Purchase Order', 'Sales Order', 'Sales Invoice', 'Purchase Invoice']:
pe.project = (doc.get('project') or
reduce(lambda prev,cur: prev or cur, [x.get('project') for x in doc.get('items')], None)) # get first non-empty project from items
if pe.party_type in ["Customer", "Supplier"]: if pe.party_type in ["Customer", "Supplier"]:
bank_account = get_party_bank_account(pe.party_type, pe.party) bank_account = get_party_bank_account(pe.party_type, pe.party)
pe.set("bank_account", bank_account) pe.set("bank_account", bank_account)
@ -1708,7 +1713,10 @@ def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outsta
def apply_early_payment_discount(paid_amount, received_amount, doc): def apply_early_payment_discount(paid_amount, received_amount, doc):
total_discount = 0 total_discount = 0
if doc.doctype in ['Sales Invoice', 'Purchase Invoice'] and doc.payment_schedule: eligible_for_payments = ['Sales Order', 'Sales Invoice', 'Purchase Order', 'Purchase Invoice']
has_payment_schedule = hasattr(doc, 'payment_schedule') and doc.payment_schedule
if doc.doctype in eligible_for_payments and has_payment_schedule:
for term in doc.payment_schedule: for term in doc.payment_schedule:
if not term.discounted_amount and term.discount and getdate(nowdate()) <= term.discount_date: if not term.discounted_amount and term.discount and getdate(nowdate()) <= term.discount_date:
if term.discount_type == 'Percentage': if term.discount_type == 'Percentage':

View File

@ -353,7 +353,6 @@ class POSInvoice(SalesInvoice):
if not for_validate and not self.customer: if not for_validate and not self.customer:
self.customer = profile.customer self.customer = profile.customer
self.ignore_pricing_rule = profile.ignore_pricing_rule
self.account_for_change_amount = profile.get('account_for_change_amount') or self.account_for_change_amount self.account_for_change_amount = profile.get('account_for_change_amount') or self.account_for_change_amount
self.set_warehouse = profile.get('warehouse') or self.set_warehouse self.set_warehouse = profile.get('warehouse') or self.set_warehouse

View File

@ -556,6 +556,37 @@ class TestPOSInvoice(unittest.TestCase):
batch.cancel() batch.cancel()
batch.delete() batch.delete()
def test_ignore_pricing_rule(self):
from erpnext.accounts.doctype.pricing_rule.test_pricing_rule import make_pricing_rule
item_price = frappe.get_doc({
'doctype': 'Item Price',
'item_code': '_Test Item',
'price_list': '_Test Price List',
'price_list_rate': '450',
})
item_price.insert()
pr = make_pricing_rule(selling=1, priority=5, discount_percentage=10)
pr.save()
pos_inv = create_pos_invoice(qty=1, do_not_submit=1)
pos_inv.items[0].rate = 300
pos_inv.save()
self.assertEquals(pos_inv.items[0].discount_percentage, 10)
# rate shouldn't change
self.assertEquals(pos_inv.items[0].rate, 405)
pos_inv.ignore_pricing_rule = 1
pos_inv.items[0].rate = 300
pos_inv.save()
self.assertEquals(pos_inv.ignore_pricing_rule, 1)
# rate should change since pricing rules are ignored
self.assertEquals(pos_inv.items[0].rate, 300)
item_price.delete()
pos_inv.delete()
pr.delete()
def create_pos_invoice(**args): def create_pos_invoice(**args):
args = frappe._dict(args) args = frappe._dict(args)
pos_profile = None pos_profile = None

View File

@ -650,7 +650,7 @@ def make_pricing_rule(**args):
"rate": args.rate or 0.0, "rate": args.rate or 0.0,
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0, "margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
"condition": args.condition or '', "condition": args.condition or '',
"priority": 1, "priority": args.priority or 1,
"discount_amount": args.discount_amount or 0.0, "discount_amount": args.discount_amount or 0.0,
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0 "apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
}) })
@ -676,6 +676,8 @@ def make_pricing_rule(**args):
if args.get(applicable_for): if args.get(applicable_for):
doc.db_set(applicable_for, args.get(applicable_for)) doc.db_set(applicable_for, args.get(applicable_for))
return doc
def setup_pricing_rule_data(): def setup_pricing_rule_data():
if not frappe.db.exists('Campaign', '_Test Campaign'): if not frappe.db.exists('Campaign', '_Test Campaign'):
frappe.get_doc({ frappe.get_doc({

View File

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

View File

@ -651,7 +651,7 @@
"hide_seconds": 1, "hide_seconds": 1,
"label": "Ignore Pricing Rule", "label": "Ignore Pricing Rule",
"no_copy": 1, "no_copy": 1,
"permlevel": 1, "permlevel": 0,
"print_hide": 1 "print_hide": 1
}, },
{ {
@ -2038,7 +2038,7 @@
"link_fieldname": "consolidated_invoice" "link_fieldname": "consolidated_invoice"
} }
], ],
"modified": "2021-10-21 20:19:38.667508", "modified": "2021-12-23 20:19:38.667508",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@ -43,6 +43,7 @@ from erpnext.setup.doctype.company.company import update_company_current_month_s
from erpnext.stock.doctype.batch.batch import set_batch_nos from erpnext.stock.doctype.batch.batch import set_batch_nos
from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so
from erpnext.stock.doctype.serial_no.serial_no import get_delivery_note_serial_no, get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_delivery_note_serial_no, get_serial_nos
from erpnext.stock.utils import calculate_mapped_packed_items_return
form_grid_templates = { form_grid_templates = {
"items": "templates/form_grid/item_grid.html" "items": "templates/form_grid/item_grid.html"
@ -728,8 +729,11 @@ class SalesInvoice(SellingController):
def update_packing_list(self): def update_packing_list(self):
if cint(self.update_stock) == 1: if cint(self.update_stock) == 1:
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list if cint(self.is_return) and self.return_against:
make_packing_list(self) calculate_mapped_packed_items_return(self)
else:
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self)
else: else:
self.set('packed_items', []) self.set('packed_items', [])
@ -862,11 +866,11 @@ class SalesInvoice(SellingController):
# Checked both rounding_adjustment and rounded_total # Checked both rounding_adjustment and rounded_total
# because rounded_total had value even before introcution of posting GLE based on 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 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(): if grand_total and not self.is_internal_transfer():
# Didnot use base_grand_total to book rounding loss gle # 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( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": self.debit_to, "account": self.debit_to,
@ -874,8 +878,8 @@ class SalesInvoice(SellingController):
"party": self.customer, "party": self.customer,
"due_date": self.due_date, "due_date": self.due_date,
"against": self.against_income_account, "against": self.against_income_account,
"debit": grand_total_in_company_currency, "debit": base_grand_total,
"debit_in_account_currency": grand_total_in_company_currency \ "debit_in_account_currency": base_grand_total \
if self.party_account_currency==self.company_currency else 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": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,

View File

@ -1781,47 +1781,6 @@ class TestSalesInvoice(unittest.TestCase):
check_gl_entries(self, si.name, expected_gle, "2019-01-30") check_gl_entries(self, si.name, expected_gle, "2019-01-30")
def test_deferred_revenue_post_account_freeze_upto_by_admin(self):
frappe.set_user("Administrator")
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', None)
deferred_account = create_account(account_name="Deferred Revenue",
parent_account="Current Liabilities - _TC", company="_Test Company")
item = create_item("_Test Item for Deferred Accounting")
item.enable_deferred_revenue = 1
item.deferred_revenue_account = deferred_account
item.no_of_months = 12
item.save()
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_save=True)
si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2019-01-10"
si.items[0].service_end_date = "2019-03-15"
si.items[0].deferred_revenue_account = deferred_account
si.save()
si.submit()
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', getdate('2019-01-31'))
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', 'System Manager')
pda1 = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date=nowdate(),
start_date="2019-01-01",
end_date="2019-03-31",
type="Income",
company="_Test Company"
))
pda1.insert()
self.assertRaises(frappe.ValidationError, pda1.submit)
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', None)
def test_fixed_deferred_revenue(self): def test_fixed_deferred_revenue(self):
deferred_account = create_account(account_name="Deferred Revenue", deferred_account = create_account(account_name="Deferred Revenue",
parent_account="Current Liabilities - _TC", company="_Test Company") parent_account="Current Liabilities - _TC", company="_Test Company")
@ -2233,9 +2192,9 @@ class TestSalesInvoice(unittest.TestCase):
asset.load_from_db() asset.load_from_db()
expected_values = [ expected_values = [
["2020-06-30", 1311.48, 1311.48], ["2020-06-30", 1366.12, 1366.12],
["2021-06-30", 20000.0, 21311.48], ["2021-06-30", 20000.0, 21366.12],
["2021-09-30", 5041.1, 26352.58] ["2021-09-30", 5041.1, 26407.22]
] ]
for i, schedule in enumerate(asset.schedules): for i, schedule in enumerate(asset.schedules):
@ -2283,12 +2242,12 @@ class TestSalesInvoice(unittest.TestCase):
asset.load_from_db() asset.load_from_db()
expected_values = [ expected_values = [
["2020-06-30", 1311.48, 1311.48, True], ["2020-06-30", 1366.12, 1366.12, True],
["2021-06-30", 20000.0, 21311.48, True], ["2021-06-30", 20000.0, 21366.12, True],
["2022-06-30", 20000.0, 41311.48, False], ["2022-06-30", 20000.0, 41366.12, False],
["2023-06-30", 20000.0, 61311.48, False], ["2023-06-30", 20000.0, 61366.12, False],
["2024-06-30", 20000.0, 81311.48, False], ["2024-06-30", 20000.0, 81366.12, False],
["2025-06-06", 18688.52, 100000.0, False] ["2025-06-06", 18633.88, 100000.0, False]
] ]
for i, schedule in enumerate(asset.schedules): for i, schedule in enumerate(asset.schedules):
@ -2482,6 +2441,74 @@ class TestSalesInvoice(unittest.TestCase):
frappe.db.set_value('Accounts Settings', None, 'over_billing_allowance', over_billing_allowance) frappe.db.set_value('Accounts Settings', None, 'over_billing_allowance', over_billing_allowance)
def test_multi_currency_deferred_revenue_via_journal_entry(self):
deferred_account = create_account(account_name="Deferred Revenue",
parent_account="Current Liabilities - _TC", company="_Test Company")
acc_settings = frappe.get_single('Accounts Settings')
acc_settings.book_deferred_entries_via_journal_entry = 1
acc_settings.submit_journal_entries = 1
acc_settings.save()
item = create_item("_Test Item for Deferred Accounting")
item.enable_deferred_expense = 1
item.deferred_revenue_account = deferred_account
item.save()
si = create_sales_invoice(customer='_Test Customer USD', currency='USD',
item=item.name, qty=1, rate=100, conversion_rate=60, do_not_save=True)
si.set_posting_time = 1
si.posting_date = '2019-01-01'
si.debit_to = '_Test Receivable USD - _TC'
si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2019-01-01"
si.items[0].service_end_date = "2019-03-30"
si.items[0].deferred_expense_account = deferred_account
si.save()
si.submit()
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', getdate('2019-01-31'))
pda1 = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date=nowdate(),
start_date="2019-01-01",
end_date="2019-03-31",
type="Income",
company="_Test Company"
))
pda1.insert()
pda1.submit()
expected_gle = [
["Sales - _TC", 0.0, 2089.89, "2019-01-28"],
[deferred_account, 2089.89, 0.0, "2019-01-28"],
["Sales - _TC", 0.0, 1887.64, "2019-02-28"],
[deferred_account, 1887.64, 0.0, "2019-02-28"],
["Sales - _TC", 0.0, 2022.47, "2019-03-15"],
[deferred_account, 2022.47, 0.0, "2019-03-15"]
]
gl_entries = gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
from `tabGL Entry`
where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
order by posting_date asc, account asc""", (si.items[0].name, si.posting_date), as_dict=1)
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_gle[i][0], gle.account)
self.assertEqual(expected_gle[i][1], gle.credit)
self.assertEqual(expected_gle[i][2], gle.debit)
self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
acc_settings = frappe.get_single('Accounts Settings')
acc_settings.book_deferred_entries_via_journal_entry = 0
acc_settings.submit_journal_entriessubmit_journal_entries = 0
acc_settings.save()
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
def get_sales_invoice_for_e_invoice(): def get_sales_invoice_for_e_invoice():
si = make_sales_invoice_for_ewaybill() si = make_sales_invoice_for_ewaybill()
si.naming_series = 'INV-2020-.#####' si.naming_series = 'INV-2020-.#####'

View File

@ -28,14 +28,14 @@
{ {
"columns": 2, "columns": 2,
"fieldname": "single_threshold", "fieldname": "single_threshold",
"fieldtype": "Currency", "fieldtype": "Float",
"in_list_view": 1, "in_list_view": 1,
"label": "Single Transaction Threshold" "label": "Single Transaction Threshold"
}, },
{ {
"columns": 3, "columns": 3,
"fieldname": "cumulative_threshold", "fieldname": "cumulative_threshold",
"fieldtype": "Currency", "fieldtype": "Float",
"in_list_view": 1, "in_list_view": 1,
"label": "Cumulative Transaction Threshold" "label": "Cumulative Transaction Threshold"
}, },
@ -59,7 +59,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2021-08-31 11:42:12.213977", "modified": "2022-01-13 12:04:42.904263",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Tax Withholding Rate", "name": "Tax Withholding Rate",
@ -68,5 +68,6 @@
"quick_entry": 1, "quick_entry": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@ -2,15 +2,17 @@
"creation": "2021-08-24 12:28:18.044902", "creation": "2021-08-24 12:28:18.044902",
"docstatus": 0, "docstatus": 0,
"doctype": "Form Tour", "doctype": "Form Tour",
"first_document": 0,
"idx": 0, "idx": 0,
"include_name_field": 0,
"is_standard": 1, "is_standard": 1,
"modified": "2021-08-24 12:28:18.044902", "modified": "2022-01-18 18:32:17.102330",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Taxes and Charges Template", "name": "Sales Taxes and Charges Template",
"owner": "Administrator", "owner": "Administrator",
"reference_doctype": "Sales Taxes and Charges Template", "reference_doctype": "Sales Taxes and Charges Template",
"save_on_complete": 0, "save_on_complete": 1,
"steps": [ "steps": [
{ {
"description": "A name by which you will identify this template. You can change this later.", "description": "A name by which you will identify this template. You can change this later.",

View File

@ -13,15 +13,12 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts", "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts",
"idx": 0, "idx": 0,
"is_complete": 0, "is_complete": 0,
"modified": "2021-08-13 11:59:35.690443", "modified": "2022-01-18 18:35:52.326688",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounts", "name": "Accounts",
"owner": "Administrator", "owner": "Administrator",
"steps": [ "steps": [
{
"step": "Company"
},
{ {
"step": "Chart of Accounts" "step": "Chart of Accounts"
}, },

View File

@ -1,22 +0,0 @@
{
"action": "Go to Page",
"action_label": "Let's Review your Company",
"creation": "2021-06-29 14:47:42.497318",
"description": "# Company\n\nIn ERPNext, you can also create multiple companies, and establish relationships (group/subsidiary) among them.\n\nWithin the company master, you can capture various default accounts for that Company and set crucial settings related to the accounting methodology followed for a company. \n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2021-08-13 11:43:35.767341",
"modified_by": "Administrator",
"name": "Company",
"owner": "Administrator",
"path": "app/company",
"reference_document": "Company",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Review Company",
"validate_action": 1
}

View File

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

View File

@ -4,7 +4,8 @@
import frappe import frappe
from frappe import _, scrub 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.party import get_partywise_advanced_payment_amount
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport 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, party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {} 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: if party_dict.outstanding == 0:
continue continue
@ -55,6 +59,10 @@ class AccountsReceivableSummary(ReceivablePayableReport):
# but in summary report advance shown in separate column # but in summary report advance shown in separate column
row.paid -= row.advance 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) self.data.append(row)
def get_party_total(self, args): 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(_(credit_debit_label), fieldname='credit_note')
self.add_column(_('Outstanding Amount'), fieldname='outstanding') 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() self.setup_ageing_columns()
if self.party_type == "Customer": if self.party_type == "Customer":
@ -140,3 +152,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
# Add column for total due amount # Add column for total due amount
self.add_column(label="Total Amount Due", fieldname='total_due') 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

@ -121,20 +121,21 @@ class Deferred_Item(object):
""" """
simulate future posting by creating dummy gl entries. starts from the last posting date. simulate future posting by creating dummy gl entries. starts from the last posting date.
""" """
if add_days(self.last_entry_date, 1) < self.period_list[-1].to_date: if self.service_start_date != self.service_end_date:
self.estimate_for_period_list = get_period_list( if add_days(self.last_entry_date, 1) < self.period_list[-1].to_date:
self.filters.from_fiscal_year, self.estimate_for_period_list = get_period_list(
self.filters.to_fiscal_year, self.filters.from_fiscal_year,
add_days(self.last_entry_date, 1), self.filters.to_fiscal_year,
self.period_list[-1].to_date, add_days(self.last_entry_date, 1),
"Date Range", self.period_list[-1].to_date,
"Monthly", "Date Range",
company=self.filters.company, "Monthly",
) company=self.filters.company,
for period in self.estimate_for_period_list: )
amount = self.calculate_amount(period.from_date, period.to_date) for period in self.estimate_for_period_list:
gle = self.make_dummy_gle(period.key, period.to_date, amount) amount = self.calculate_amount(period.from_date, period.to_date)
self.gle_entries.append(gle) gle = self.make_dummy_gle(period.key, period.to_date, amount)
self.gle_entries.append(gle)
def calculate_item_revenue_expense_for_period(self): def calculate_item_revenue_expense_for_period(self):
""" """

View File

@ -167,7 +167,7 @@ frappe.query_reports["General Ledger"] = {
"fieldname": "include_dimensions", "fieldname": "include_dimensions",
"label": __("Consider Accounting Dimensions"), "label": __("Consider Accounting Dimensions"),
"fieldtype": "Check", "fieldtype": "Check",
"default": 0 "default": 1
}, },
{ {
"fieldname": "show_opening_entries", "fieldname": "show_opening_entries",

View File

@ -448,9 +448,11 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map):
elif group_by_voucher_consolidated: elif group_by_voucher_consolidated:
keylist = [gle.get("voucher_type"), gle.get("voucher_no"), gle.get("account")] keylist = [gle.get("voucher_type"), gle.get("voucher_no"), gle.get("account")]
for dim in accounting_dimensions: if filters.get("include_dimensions"):
keylist.append(gle.get(dim)) for dim in accounting_dimensions:
keylist.append(gle.get("cost_center")) keylist.append(gle.get(dim))
keylist.append(gle.get("cost_center"))
key = tuple(keylist) key = tuple(keylist)
if key not in consolidated_gle: if key not in consolidated_gle:
consolidated_gle.setdefault(key, gle) consolidated_gle.setdefault(key, gle)
@ -547,10 +549,7 @@ def get_columns(filters):
"fieldname": "balance", "fieldname": "balance",
"fieldtype": "Float", "fieldtype": "Float",
"width": 130 "width": 130
} },
]
columns.extend([
{ {
"label": _("Voucher Type"), "label": _("Voucher Type"),
"fieldname": "voucher_type", "fieldname": "voucher_type",
@ -584,7 +583,7 @@ def get_columns(filters):
"fieldname": "project", "fieldname": "project",
"width": 100 "width": 100
} }
]) ]
if filters.get("include_dimensions"): if filters.get("include_dimensions"):
for dim in get_accounting_dimensions(as_list = False): for dim in get_accounting_dimensions(as_list = False):
@ -594,14 +593,14 @@ def get_columns(filters):
"fieldname": dim.fieldname, "fieldname": dim.fieldname,
"width": 100 "width": 100
}) })
columns.append({
columns.extend([
{
"label": _("Cost Center"), "label": _("Cost Center"),
"options": "Cost Center", "options": "Cost Center",
"fieldname": "cost_center", "fieldname": "cost_center",
"width": 100 "width": 100
}, })
columns.extend([
{ {
"label": _("Against Voucher Type"), "label": _("Against Voucher Type"),
"fieldname": "against_voucher_type", "fieldname": "against_voucher_type",

View File

@ -0,0 +1,48 @@
import unittest
from typing import List, Tuple
from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report
DEFAULT_FILTERS = {
"company": "_Test Company",
"from_date": "2010-01-01",
"to_date": "2030-01-01",
"period_start_date": "2010-01-01",
"period_end_date": "2030-01-01"
}
REPORT_FILTER_TEST_CASES: List[Tuple[ReportName, ReportFilters]] = [
("General Ledger", {"group_by": "Group by Voucher (Consolidated)"} ),
("General Ledger", {"group_by": "Group by Voucher (Consolidated)", "include_dimensions": 1} ),
("Accounts Payable", {"range1": 30, "range2": 60, "range3": 90, "range4": 120}),
("Accounts Receivable", {"range1": 30, "range2": 60, "range3": 90, "range4": 120}),
("Consolidated Financial Statement", {"report": "Balance Sheet"} ),
("Consolidated Financial Statement", {"report": "Profit and Loss Statement"} ),
("Consolidated Financial Statement", {"report": "Cash Flow"} ),
("Gross Profit", {"group_by": "Invoice"}),
("Gross Profit", {"group_by": "Item Code"}),
("Gross Profit", {"group_by": "Item Group"}),
("Gross Profit", {"group_by": "Customer"}),
("Gross Profit", {"group_by": "Customer Group"}),
("Item-wise Sales Register", {}),
("Item-wise Purchase Register", {}),
("Sales Register", {}),
("Purchase Register", {}),
("Tax Detail", {"mode": "run", "report_name": "Tax Detail"},),
]
OPTIONAL_FILTERS = {}
class TestReports(unittest.TestCase):
def test_execute_all_accounts_reports(self):
"""Test that all script report in stock modules are executable with supported filters"""
for report, filter in REPORT_FILTER_TEST_CASES:
execute_script_report(
report_name=report,
module="Accounts",
filters=filter,
default_filters=DEFAULT_FILTERS,
optional_filters=OPTIONAL_FILTERS if filter.get("_optional") else None,
)

View File

@ -5,7 +5,7 @@
"label": "Profit and Loss" "label": "Profit and Loss"
} }
], ],
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Accounts\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Profit and Loss\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chart of Accounts\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Invoice\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Invoice\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Journal Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Payment Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Accounts Receivable\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"General Ledger\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Trial Balance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounting Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"General Ledger\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounts Receivable\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounts Payable\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Financial Statements\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Multi Currency\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Bank Statement\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Subscription Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Goods and Services Tax (GST India)\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Share Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Cost Center and Budgeting\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Opening and Closing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Taxes\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Profitability\", \"col\": 4}}]", "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chart of Accounts\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"General Ledger\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Trial Balance\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"General Ledger\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Receivable\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Payable\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Multi Currency\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Bank Statement\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Subscription Management\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Goods and Services Tax (GST India)\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Share Management\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Cost Center and Budgeting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Opening and Closing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Taxes\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}}]",
"creation": "2020-03-02 15:41:59.515192", "creation": "2020-03-02 15:41:59.515192",
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
@ -230,6 +230,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Payment Reconciliation", "label": "Payment Reconciliation",
"link_count": 0,
"link_to": "Payment Reconciliation", "link_to": "Payment Reconciliation",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -346,6 +347,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Payment Reconciliation", "label": "Payment Reconciliation",
"link_count": 0,
"link_to": "Payment Reconciliation", "link_to": "Payment Reconciliation",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -527,16 +529,17 @@
"type": "Link" "type": "Link"
}, },
{ {
"dependencies": "GL Entry", "dependencies": "GL Entry",
"hidden": 0, "hidden": 0,
"is_query_report": 1, "is_query_report": 1,
"label": "KSA VAT Report", "label": "KSA VAT Report",
"link_to": "KSA VAT", "link_count": 0,
"link_type": "Report", "link_to": "KSA VAT",
"onboard": 0, "link_type": "Report",
"only_for": "Saudi Arabia", "onboard": 0,
"type": "Link" "only_for": "Saudi Arabia",
}, "type": "Link"
},
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
@ -1158,15 +1161,16 @@
"type": "Link" "type": "Link"
}, },
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "KSA VAT Setting", "label": "KSA VAT Setting",
"link_to": "KSA VAT Setting", "link_count": 0,
"link_type": "DocType", "link_to": "KSA VAT Setting",
"onboard": 0, "link_type": "DocType",
"only_for": "Saudi Arabia", "onboard": 0,
"type": "Link" "only_for": "Saudi Arabia",
}, "type": "Link"
},
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
@ -1220,7 +1224,7 @@
"type": "Link" "type": "Link"
} }
], ],
"modified": "2021-08-27 12:15:52.872471", "modified": "2022-01-13 17:25:09.835345",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounting", "name": "Accounting",
@ -1229,7 +1233,7 @@
"public": 1, "public": 1,
"restrict_to_domain": "", "restrict_to_domain": "",
"roles": [], "roles": [],
"sequence_id": 2, "sequence_id": 2.0,
"shortcuts": [ "shortcuts": [
{ {
"label": "Chart of Accounts", "label": "Chart of Accounts",
@ -1278,4 +1282,4 @@
} }
], ],
"title": "Accounting" "title": "Accounting"
} }

View File

@ -35,6 +35,7 @@
"available_for_use_date", "available_for_use_date",
"column_break_23", "column_break_23",
"gross_purchase_amount", "gross_purchase_amount",
"asset_quantity",
"purchase_date", "purchase_date",
"section_break_23", "section_break_23",
"calculate_depreciation", "calculate_depreciation",
@ -480,6 +481,12 @@
"fieldname": "section_break_36", "fieldname": "section_break_36",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Finance Books" "label": "Finance Books"
},
{
"fieldname": "asset_quantity",
"fieldtype": "Int",
"label": "Asset Quantity",
"read_only_depends_on": "eval:!doc.is_existing_asset"
} }
], ],
"idx": 72, "idx": 72,
@ -502,10 +509,11 @@
"link_fieldname": "asset" "link_fieldname": "asset"
} }
], ],
"modified": "2021-06-24 14:58:51.097908", "modified": "2022-01-18 12:57:36.741192",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Asset", "name": "Asset",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
@ -542,6 +550,7 @@
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"title_field": "asset_name", "title_field": "asset_name",
"track_changes": 1 "track_changes": 1
} }

View File

@ -36,6 +36,7 @@ class Asset(AccountsController):
self.validate_asset_values() self.validate_asset_values()
self.validate_asset_and_reference() self.validate_asset_and_reference()
self.validate_item() self.validate_item()
self.validate_cost_center()
self.set_missing_values() self.set_missing_values()
self.prepare_depreciation_data() self.prepare_depreciation_data()
self.validate_gross_and_purchase_amount() self.validate_gross_and_purchase_amount()
@ -95,6 +96,19 @@ class Asset(AccountsController):
elif item.is_stock_item: elif item.is_stock_item:
frappe.throw(_("Item {0} must be a non-stock item").format(self.item_code)) frappe.throw(_("Item {0} must be a non-stock item").format(self.item_code))
def validate_cost_center(self):
if not self.cost_center: return
cost_center_company = frappe.db.get_value('Cost Center', self.cost_center, 'company')
if cost_center_company != self.company:
frappe.throw(
_("Selected Cost Center {} doesn't belongs to {}").format(
frappe.bold(self.cost_center),
frappe.bold(self.company)
),
title=_("Invalid Cost Center")
)
def validate_in_use_date(self): def validate_in_use_date(self):
if not self.available_for_use_date: if not self.available_for_use_date:
frappe.throw(_("Available for use date is required")) frappe.throw(_("Available for use date is required"))
@ -242,8 +256,9 @@ class Asset(AccountsController):
# For first row # For first row
if has_pro_rata and not self.opening_accumulated_depreciation and n==0: if has_pro_rata and not self.opening_accumulated_depreciation and n==0:
from_date = add_days(self.available_for_use_date, -1) # needed to calc depr amount for available_for_use_date too
depreciation_amount, days, months = self.get_pro_rata_amt(finance_book, depreciation_amount, depreciation_amount, days, months = self.get_pro_rata_amt(finance_book, depreciation_amount,
self.available_for_use_date, finance_book.depreciation_start_date) from_date, finance_book.depreciation_start_date)
# For first depr schedule date will be the start date # For first depr schedule date will be the start date
# so monthly schedule date is calculated by removing month difference between use date and start date # so monthly schedule date is calculated by removing month difference between use date and start date
@ -374,7 +389,9 @@ class Asset(AccountsController):
if from_date: if from_date:
return from_date return from_date
return self.available_for_use_date
# since depr for available_for_use_date is not yet booked
return add_days(self.available_for_use_date, -1)
# if it returns True, depreciation_amount will not be equal for the first and last rows # if it returns True, depreciation_amount will not be equal for the first and last rows
def check_is_pro_rata(self, row): def check_is_pro_rata(self, row):
@ -608,7 +625,17 @@ class Asset(AccountsController):
return purchase_document return purchase_document
def get_fixed_asset_account(self): def get_fixed_asset_account(self):
return get_asset_category_account('fixed_asset_account', None, self.name, None, self.asset_category, self.company) fixed_asset_account = get_asset_category_account('fixed_asset_account', None, self.name, None, self.asset_category, self.company)
if not fixed_asset_account:
frappe.throw(
_("Set {0} in asset category {1} for company {2}").format(
frappe.bold("Fixed Asset Account"),
frappe.bold(self.asset_category),
frappe.bold(self.company),
),
title=_("Account not Found"),
)
return fixed_asset_account
def get_cwip_account(self, cwip_enabled=False): def get_cwip_account(self, cwip_enabled=False):
cwip_account = None cwip_account = None

View File

@ -134,6 +134,29 @@ class TestAsset(AssetSetup):
pr.cancel() pr.cancel()
self.assertEqual(asset.docstatus, 2) self.assertEqual(asset.docstatus, 2)
def test_purchase_of_grouped_asset(self):
create_fixed_asset_item("Rack", is_grouped_asset=1)
pr = make_purchase_receipt(item_code="Rack", qty=3, rate=100000.0, location="Test Location")
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
asset = frappe.get_doc('Asset', asset_name)
self.assertEqual(asset.asset_quantity, 3)
asset.calculate_depreciation = 1
month_end_date = get_last_day(nowdate())
purchase_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
asset.available_for_use_date = purchase_date
asset.purchase_date = purchase_date
asset.append("finance_books", {
"expected_value_after_useful_life": 10000,
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 3,
"frequency_of_depreciation": 10,
"depreciation_start_date": month_end_date
})
asset.submit()
def test_is_fixed_asset_set(self): def test_is_fixed_asset_set(self):
asset = create_asset(is_existing_asset = 1) asset = create_asset(is_existing_asset = 1)
doc = frappe.new_doc('Purchase Invoice') doc = frappe.new_doc('Purchase Invoice')
@ -207,9 +230,9 @@ class TestAsset(AssetSetup):
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold") self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
expected_gle = ( expected_gle = (
("_Test Accumulated Depreciations - _TC", 20392.16, 0.0), ("_Test Accumulated Depreciations - _TC", 20490.2, 0.0),
("_Test Fixed Asset - _TC", 0.0, 100000.0), ("_Test Fixed Asset - _TC", 0.0, 100000.0),
("_Test Gain/Loss on Asset Disposal - _TC", 54607.84, 0.0), ("_Test Gain/Loss on Asset Disposal - _TC", 54509.8, 0.0),
("Debtors - _TC", 25000.0, 0.0) ("Debtors - _TC", 25000.0, 0.0)
) )
@ -491,10 +514,10 @@ class TestDepreciationMethods(AssetSetup):
) )
expected_schedules = [ expected_schedules = [
["2030-12-31", 27534.25, 27534.25], ['2030-12-31', 27616.44, 27616.44],
["2031-12-31", 30000.0, 57534.25], ['2031-12-31', 30000.0, 57616.44],
["2032-12-31", 30000.0, 87534.25], ['2032-12-31', 30000.0, 87616.44],
["2033-01-30", 2465.75, 90000.0] ['2033-01-30', 2383.56, 90000.0]
] ]
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
@ -544,10 +567,10 @@ class TestDepreciationMethods(AssetSetup):
self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
expected_schedules = [ expected_schedules = [
["2030-12-31", 28493.15, 28493.15], ['2030-12-31', 28630.14, 28630.14],
["2031-12-31", 35753.43, 64246.58], ['2031-12-31', 35684.93, 64315.07],
["2032-12-31", 17876.71, 82123.29], ['2032-12-31', 17842.47, 82157.54],
["2033-06-06", 5376.71, 87500.0] ['2033-06-06', 5342.46, 87500.0]
] ]
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
@ -580,10 +603,10 @@ class TestDepreciationMethods(AssetSetup):
self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
expected_schedules = [ expected_schedules = [
["2030-12-31", 11780.82, 11780.82], ["2030-12-31", 11849.32, 11849.32],
["2031-12-31", 44109.59, 55890.41], ["2031-12-31", 44075.34, 55924.66],
["2032-12-31", 22054.8, 77945.21], ["2032-12-31", 22037.67, 77962.33],
["2033-07-12", 9554.79, 87500.0] ["2033-07-12", 9537.67, 87500.0]
] ]
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
@ -621,7 +644,7 @@ class TestDepreciationBasics(AssetSetup):
asset = create_asset( asset = create_asset(
item_code = "Macbook Pro", item_code = "Macbook Pro",
calculate_depreciation = 1, calculate_depreciation = 1,
available_for_use_date = getdate("2019-12-31"), available_for_use_date = getdate("2020-01-01"),
total_number_of_depreciations = 3, total_number_of_depreciations = 3,
expected_value_after_useful_life = 10000, expected_value_after_useful_life = 10000,
depreciation_start_date = getdate("2020-07-01"), depreciation_start_date = getdate("2020-07-01"),
@ -632,7 +655,7 @@ class TestDepreciationBasics(AssetSetup):
["2020-07-01", 15000, 15000], ["2020-07-01", 15000, 15000],
["2021-07-01", 30000, 45000], ["2021-07-01", 30000, 45000],
["2022-07-01", 30000, 75000], ["2022-07-01", 30000, 75000],
["2022-12-31", 15000, 90000] ["2023-01-01", 15000, 90000]
] ]
for i, schedule in enumerate(asset.schedules): for i, schedule in enumerate(asset.schedules):
@ -1109,6 +1132,7 @@ class TestDepreciationBasics(AssetSetup):
self.assertEqual(gle, expected_gle) self.assertEqual(gle, expected_gle)
self.assertEqual(asset.get("value_after_depreciation"), 0) self.assertEqual(asset.get("value_after_depreciation"), 0)
def test_expected_value_change(self): def test_expected_value_change(self):
""" """
tests if changing `expected_value_after_useful_life` tests if changing `expected_value_after_useful_life`
@ -1130,6 +1154,15 @@ class TestDepreciationBasics(AssetSetup):
asset.reload() asset.reload()
self.assertEquals(asset.finance_books[0].value_after_depreciation, 98000.0) self.assertEquals(asset.finance_books[0].value_after_depreciation, 98000.0)
def test_asset_cost_center(self):
asset = create_asset(is_existing_asset = 1, do_not_save=1)
asset.cost_center = "Main - WP"
self.assertRaises(frappe.ValidationError, asset.submit)
asset.cost_center = "Main - _TC"
asset.submit()
def create_asset_data(): def create_asset_data():
if not frappe.db.exists("Asset Category", "Computers"): if not frappe.db.exists("Asset Category", "Computers"):
create_asset_category() create_asset_category()
@ -1202,13 +1235,13 @@ def create_asset_category():
}) })
asset_category.insert() asset_category.insert()
def create_fixed_asset_item(): def create_fixed_asset_item(item_code=None, auto_create_assets=1, is_grouped_asset=0):
meta = frappe.get_meta('Asset') meta = frappe.get_meta('Asset')
naming_series = meta.get_field("naming_series").options.splitlines()[0] or 'ACC-ASS-.YYYY.-' naming_series = meta.get_field("naming_series").options.splitlines()[0] or 'ACC-ASS-.YYYY.-'
try: try:
frappe.get_doc({ item = frappe.get_doc({
"doctype": "Item", "doctype": "Item",
"item_code": "Macbook Pro", "item_code": item_code or "Macbook Pro",
"item_name": "Macbook Pro", "item_name": "Macbook Pro",
"description": "Macbook Pro Retina Display", "description": "Macbook Pro Retina Display",
"asset_category": "Computers", "asset_category": "Computers",
@ -1216,11 +1249,14 @@ def create_fixed_asset_item():
"stock_uom": "Nos", "stock_uom": "Nos",
"is_stock_item": 0, "is_stock_item": 0,
"is_fixed_asset": 1, "is_fixed_asset": 1,
"auto_create_assets": 1, "auto_create_assets": auto_create_assets,
"is_grouped_asset": is_grouped_asset,
"asset_naming_series": naming_series "asset_naming_series": naming_series
}).insert() })
item.insert()
except frappe.DuplicateEntryError: except frappe.DuplicateEntryError:
pass pass
return item
def set_depreciation_settings_in_company(): def set_depreciation_settings_in_company():
company = frappe.get_doc("Company", "_Test Company") company = frappe.get_doc("Company", "_Test Company")

View File

@ -5,7 +5,7 @@
"label": "Asset Value Analytics" "label": "Asset Value Analytics"
} }
], ],
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Assets\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Asset Value Analytics\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Asset\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Asset Category\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Fixed Asset Register\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assets\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]", "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Assets\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Asset Value Analytics\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Asset\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Asset Category\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Fixed Asset Register\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Assets\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Maintenance\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
"creation": "2020-03-02 15:43:27.634865", "creation": "2020-03-02 15:43:27.634865",
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
@ -172,7 +172,7 @@
"type": "Link" "type": "Link"
} }
], ],
"modified": "2021-08-05 12:15:54.839453", "modified": "2022-01-13 17:25:41.730628",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Assets", "name": "Assets",
@ -181,7 +181,7 @@
"public": 1, "public": 1,
"restrict_to_domain": "", "restrict_to_domain": "",
"roles": [], "roles": [],
"sequence_id": 4, "sequence_id": 4.0,
"shortcuts": [ "shortcuts": [
{ {
"label": "Asset", "label": "Asset",

View File

@ -5,7 +5,7 @@
"label": "Purchase Order Trends" "label": "Purchase Order Trends"
} }
], ],
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Buying\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Purchase Order Trends\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Material Request\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Order Analysis\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Buying\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items & Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Supplier\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Supplier Scorecard\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Regional\", \"col\": 4}}]", "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Buying\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Purchase Order Trends\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Material Request\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Order Analysis\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Buying\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items & Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Supplier\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Supplier Scorecard\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Regional\",\"col\":4}}]",
"creation": "2020-01-28 11:50:26.195467", "creation": "2020-01-28 11:50:26.195467",
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
@ -509,7 +509,7 @@
"type": "Link" "type": "Link"
} }
], ],
"modified": "2021-08-05 12:15:56.218428", "modified": "2022-01-13 17:26:39.090190",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Buying", "name": "Buying",
@ -518,7 +518,7 @@
"public": 1, "public": 1,
"restrict_to_domain": "", "restrict_to_domain": "",
"roles": [], "roles": [],
"sequence_id": 6, "sequence_id": 6.0,
"shortcuts": [ "shortcuts": [
{ {
"color": "Green", "color": "Green",

View File

@ -1,49 +1,10 @@
# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt # GPL v3 License. See license.txt
import click import click
import frappe
from frappe.commands import get_site, pass_context
def call_command(cmd, context): def call_command(cmd, context):
return click.Context(cmd, obj=context).forward(cmd) return click.Context(cmd, obj=context).forward(cmd)
@click.command('make-demo') commands = []
@click.option('--site', help='site name')
@click.option('--domain', default='Manufacturing')
@click.option('--days', default=100,
help='Run the demo for so many days. Default 100')
@click.option('--resume', default=False, is_flag=True,
help='Continue running the demo for given days')
@click.option('--reinstall', default=False, is_flag=True,
help='Reinstall site before demo')
@pass_context
def make_demo(context, site, domain='Manufacturing', days=100,
resume=False, reinstall=False):
"Reinstall site and setup demo"
from frappe.commands.site import _reinstall
from frappe.installer import install_app
site = get_site(context)
if resume:
with frappe.init_site(site):
frappe.connect()
from erpnext.demo import demo
demo.simulate(days=days)
else:
if reinstall:
_reinstall(site, yes=True)
with frappe.init_site(site=site):
frappe.connect()
if not 'erpnext' in frappe.get_installed_apps():
install_app('erpnext')
# import needs site
from erpnext.demo import demo
demo.make(domain, days)
commands = [
make_demo
]

View File

@ -113,7 +113,7 @@ class AccountsController(TransactionBase):
_('{0} is blocked so this transaction cannot proceed').format(supplier_name), raise_exception=1) _('{0} is blocked so this transaction cannot proceed').format(supplier_name), raise_exception=1)
def validate(self): def validate(self):
if not self.get('is_return'): if not self.get('is_return') and not self.get('is_debit_note'):
self.validate_qty_is_not_zero() self.validate_qty_is_not_zero()
if self.get("_action") and self._action != "update_after_submit": if self.get("_action") and self._action != "update_after_submit":
@ -185,8 +185,6 @@ class AccountsController(TransactionBase):
frappe.throw(_("Row #{0}: Service Start Date cannot be greater than Service End Date").format(d.idx)) frappe.throw(_("Row #{0}: Service Start Date cannot be greater than Service End Date").format(d.idx))
elif getdate(self.posting_date) > getdate(d.service_end_date): elif getdate(self.posting_date) > getdate(d.service_end_date):
frappe.throw(_("Row #{0}: Service End Date cannot be before Invoice Posting Date").format(d.idx)) frappe.throw(_("Row #{0}: Service End Date cannot be before Invoice Posting Date").format(d.idx))
elif getdate(self.posting_date) > getdate(d.service_start_date):
frappe.throw(_("Row #{0}: Service Start Date cannot be before Invoice Posting Date").format(d.idx))
def validate_invoice_documents_schedule(self): def validate_invoice_documents_schedule(self):
self.validate_payment_schedule_dates() self.validate_payment_schedule_dates()

View File

@ -554,10 +554,13 @@ class BuyingController(StockController, Subcontracting):
# Check for asset naming series # Check for asset naming series
if item_data.get('asset_naming_series'): if item_data.get('asset_naming_series'):
created_assets = [] created_assets = []
if item_data.get('is_grouped_asset'):
for qty in range(cint(d.qty)): asset = self.make_asset(d, is_grouped_asset=True)
asset = self.make_asset(d)
created_assets.append(asset) created_assets.append(asset)
else:
for qty in range(cint(d.qty)):
asset = self.make_asset(d)
created_assets.append(asset)
if len(created_assets) > 5: if len(created_assets) > 5:
# dont show asset form links if more than 5 assets are created # dont show asset form links if more than 5 assets are created
@ -580,14 +583,18 @@ class BuyingController(StockController, Subcontracting):
for message in messages: for message in messages:
frappe.msgprint(message, title="Success", indicator="green") frappe.msgprint(message, title="Success", indicator="green")
def make_asset(self, row): def make_asset(self, row, is_grouped_asset=False):
if not row.asset_location: if not row.asset_location:
frappe.throw(_("Row {0}: Enter location for the asset item {1}").format(row.idx, row.item_code)) frappe.throw(_("Row {0}: Enter location for the asset item {1}").format(row.idx, row.item_code))
item_data = frappe.db.get_value('Item', item_data = frappe.db.get_value('Item',
row.item_code, ['asset_naming_series', 'asset_category'], as_dict=1) row.item_code, ['asset_naming_series', 'asset_category'], as_dict=1)
purchase_amount = flt(row.base_rate + row.item_tax_amount) if is_grouped_asset:
purchase_amount = flt(row.base_amount + row.item_tax_amount)
else:
purchase_amount = flt(row.base_rate + row.item_tax_amount)
asset = frappe.get_doc({ asset = frappe.get_doc({
'doctype': 'Asset', 'doctype': 'Asset',
'item_code': row.item_code, 'item_code': row.item_code,
@ -601,6 +608,7 @@ class BuyingController(StockController, Subcontracting):
'calculate_depreciation': 1, 'calculate_depreciation': 1,
'purchase_receipt_amount': purchase_amount, 'purchase_receipt_amount': purchase_amount,
'gross_purchase_amount': purchase_amount, 'gross_purchase_amount': purchase_amount,
'asset_quantity': row.qty if is_grouped_asset else 0,
'purchase_receipt': self.name if self.doctype == 'Purchase Receipt' else None, 'purchase_receipt': self.name if self.doctype == 'Purchase Receipt' else None,
'purchase_invoice': self.name if self.doctype == 'Purchase Invoice' else None 'purchase_invoice': self.name if self.doctype == 'Purchase Invoice' else None
}) })
@ -687,7 +695,7 @@ class BuyingController(StockController, Subcontracting):
def get_asset_item_details(asset_items): def get_asset_item_details(asset_items):
asset_items_data = {} asset_items_data = {}
for d in frappe.get_all('Item', fields = ["name", "auto_create_assets", "asset_naming_series"], for d in frappe.get_all('Item', fields = ["name", "auto_create_assets", "asset_naming_series", "is_grouped_asset"],
filters = {'name': ('in', asset_items)}): filters = {'name': ('in', asset_items)}):
asset_items_data.setdefault(d.name, d) asset_items_data.setdefault(d.name, d)

View File

@ -385,7 +385,7 @@ class SellingController(StockController):
# Get incoming rate based on original item cost based on valuation method # Get incoming rate based on original item cost based on valuation method
qty = flt(d.get('stock_qty') or d.get('actual_qty')) qty = flt(d.get('stock_qty') or d.get('actual_qty'))
if not d.incoming_rate: if not (self.get("is_return") and d.incoming_rate):
d.incoming_rate = get_incoming_rate({ d.incoming_rate = get_incoming_rate({
"item_code": d.item_code, "item_code": d.item_code,
"warehouse": d.warehouse, "warehouse": d.warehouse,

View File

@ -17,7 +17,7 @@ from erpnext.accounts.general_ledger import (
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from erpnext.controllers.accounts_controller import AccountsController from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock import get_warehouse_account_map from erpnext.stock import get_warehouse_account_map
from erpnext.stock.stock_ledger import get_items_to_be_repost, get_valuation_rate from erpnext.stock.stock_ledger import get_items_to_be_repost
class QualityInspectionRequiredError(frappe.ValidationError): pass class QualityInspectionRequiredError(frappe.ValidationError): pass
@ -111,17 +111,6 @@ class StockController(AccountsController):
self.check_expense_account(item_row) self.check_expense_account(item_row)
# If the item does not have the allow zero valuation rate flag set
# and ( valuation rate not mentioned in an incoming entry
# or incoming entry not found while delivering the item),
# try to pick valuation rate from previous sle or Item master and update in SLE
# Otherwise, throw an exception
if not sle.stock_value_difference and self.doctype != "Stock Reconciliation" \
and not item_row.get("allow_zero_valuation_rate"):
sle = self.update_stock_ledger_entries(sle)
# expense account/ target_warehouse / source_warehouse # expense account/ target_warehouse / source_warehouse
if item_row.get('target_warehouse'): if item_row.get('target_warehouse'):
warehouse = item_row.get('target_warehouse') warehouse = item_row.get('target_warehouse')
@ -164,26 +153,6 @@ class StockController(AccountsController):
return frappe.flags.debit_field_precision return frappe.flags.debit_field_precision
def update_stock_ledger_entries(self, sle):
sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
self.doctype, self.name, currency=self.company_currency, company=self.company)
sle.stock_value = flt(sle.qty_after_transaction) * flt(sle.valuation_rate)
sle.stock_value_difference = flt(sle.actual_qty) * flt(sle.valuation_rate)
if sle.name:
frappe.db.sql("""
update
`tabStock Ledger Entry`
set
stock_value = %(stock_value)s,
valuation_rate = %(valuation_rate)s,
stock_value_difference = %(stock_value_difference)s
where
name = %(name)s""", (sle))
return sle
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map): def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
if self.doctype == "Stock Reconciliation": if self.doctype == "Stock Reconciliation":
reconciliation_purpose = frappe.db.get_value(self.doctype, self.name, "purpose") reconciliation_purpose = frappe.db.get_value(self.doctype, self.name, "purpose")
@ -287,11 +256,7 @@ class StockController(AccountsController):
for d in self.items: for d in self.items:
if not d.batch_no: continue if not d.batch_no: continue
serial_nos = [sr.name for sr in frappe.get_all("Serial No", frappe.db.set_value("Serial No", {"batch_no": d.batch_no, "status": "Inactive"}, "batch_no", None)
{'batch_no': d.batch_no, 'status': 'Inactive'})]
if serial_nos:
frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None)
d.batch_no = None d.batch_no = None
d.db_set("batch_no", None) d.db_set("batch_no", None)

View File

@ -139,6 +139,8 @@ class calculate_taxes_and_totals(object):
if not item.qty and self.doc.get("is_return"): if not item.qty and self.doc.get("is_return"):
item.amount = flt(-1 * item.rate, item.precision("amount")) item.amount = flt(-1 * item.rate, item.precision("amount"))
elif not item.qty and self.doc.get("is_debit_note"):
item.amount = flt(item.rate, item.precision("amount"))
else: else:
item.amount = flt(item.rate * item.qty, item.precision("amount")) item.amount = flt(item.rate * item.qty, item.precision("amount"))
@ -594,13 +596,14 @@ class calculate_taxes_and_totals(object):
if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]: if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
grand_total = self.doc.rounded_total or self.doc.grand_total grand_total = self.doc.rounded_total or self.doc.grand_total
base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total
if self.doc.party_account_currency == self.doc.currency: if self.doc.party_account_currency == self.doc.currency:
total_amount_to_pay = flt(grand_total - self.doc.total_advance total_amount_to_pay = flt(grand_total - self.doc.total_advance
- flt(self.doc.write_off_amount), self.doc.precision("grand_total")) - flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
else: else:
total_amount_to_pay = flt(flt(grand_total * total_amount_to_pay = flt(flt(base_grand_total, self.doc.precision("base_grand_total")) - self.doc.total_advance
self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance - flt(self.doc.base_write_off_amount), self.doc.precision("base_grand_total"))
- flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
self.doc.round_floats_in(self.doc, ["paid_amount"]) self.doc.round_floats_in(self.doc, ["paid_amount"])
change_amount = 0 change_amount = 0

View File

@ -1,6 +1,8 @@
import unittest import unittest
from functools import partial from functools import partial
import frappe
from erpnext.controllers import queries from erpnext.controllers import queries
@ -85,3 +87,6 @@ class TestQueries(unittest.TestCase):
wh = query(filters=[["Bin", "item_code", "=", "_Test Item"]]) wh = query(filters=[["Bin", "item_code", "=", "_Test Item"]])
self.assertGreaterEqual(len(wh), 1) self.assertGreaterEqual(len(wh), 1)
def test_default_uoms(self):
self.assertGreaterEqual(frappe.db.count("UOM", {"enabled": 1}), 10)

View File

@ -4,19 +4,72 @@ import frappe
class TestUtils(unittest.TestCase): class TestUtils(unittest.TestCase):
def test_reset_default_field_value(self): def test_reset_default_field_value(self):
doc = frappe.get_doc({ doc = frappe.get_doc({
"doctype": "Purchase Receipt", "doctype": "Purchase Receipt",
"set_warehouse": "Warehouse 1", "set_warehouse": "Warehouse 1",
}) })
# Same values # Same values
doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}] doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}]
doc.reset_default_field_value("set_warehouse", "items", "warehouse") doc.reset_default_field_value("set_warehouse", "items", "warehouse")
self.assertEqual(doc.set_warehouse, "Warehouse 1") self.assertEqual(doc.set_warehouse, "Warehouse 1")
# Mixed values # Mixed values
doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 2"}, {"warehouse": "Warehouse 1"}] doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 2"}, {"warehouse": "Warehouse 1"}]
doc.reset_default_field_value("set_warehouse", "items", "warehouse") doc.reset_default_field_value("set_warehouse", "items", "warehouse")
self.assertEqual(doc.set_warehouse, None) self.assertEqual(doc.set_warehouse, None)
def test_reset_default_field_value_in_mfg_stock_entry(self):
# manufacture stock entry with rows having blank source/target wh
se = frappe.get_doc(
doctype="Stock Entry",
purpose="Manufacture",
stock_entry_type="Manufacture",
company="_Test Company",
from_warehouse="_Test Warehouse - _TC",
to_warehouse="_Test Warehouse 1 - _TC",
items=[
frappe._dict(item_code="_Test Item", qty=1, basic_rate=200, s_warehouse="_Test Warehouse - _TC"),
frappe._dict(item_code="_Test FG Item", qty=4, t_warehouse="_Test Warehouse 1 - _TC", is_finished_item=1)
]
)
se.save()
# default fields must be untouched
self.assertEqual(se.from_warehouse, "_Test Warehouse - _TC")
self.assertEqual(se.to_warehouse, "_Test Warehouse 1 - _TC")
se.delete()
def test_reset_default_field_value_in_transfer_stock_entry(self):
doc = frappe.get_doc({
"doctype": "Stock Entry",
"purpose": "Material Receipt",
"from_warehouse": "Warehouse 1",
"to_warehouse": "Warehouse 2",
})
# Same values
doc.items = [
{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"},
{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"},
{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"}
]
doc.reset_default_field_value("from_warehouse", "items", "s_warehouse")
doc.reset_default_field_value("to_warehouse", "items", "t_warehouse")
self.assertEqual(doc.from_warehouse, "Warehouse 1")
self.assertEqual(doc.to_warehouse, "Warehouse 2")
# Mixed values in source wh
doc.items = [
{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"},
{"s_warehouse": "Warehouse 3", "t_warehouse": "Warehouse 2"},
{"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"}
]
doc.reset_default_field_value("from_warehouse", "items", "s_warehouse")
doc.reset_default_field_value("to_warehouse", "items", "t_warehouse")
self.assertEqual(doc.from_warehouse, None)
self.assertEqual(doc.to_warehouse, "Warehouse 2")

View File

@ -1,10 +1,11 @@
{ {
"charts": [ "charts": [
{ {
"chart_name": "Territory Wise Sales" "chart_name": "Territory Wise Sales",
"label": "Territory Wise Sales"
} }
], ],
"content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"CRM\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Lead\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Opportunity\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Customer\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Sales Pipeline\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Campaign\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]", "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"CRM\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Territory Wise Sales\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Lead\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Opportunity\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Sales Pipeline\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Maintenance\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Campaign\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
"creation": "2020-01-23 14:48:30.183272", "creation": "2020-01-23 14:48:30.183272",
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
@ -144,6 +145,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 1, "is_query_report": 1,
"label": "Sales Pipeline Analytics", "label": "Sales Pipeline Analytics",
"link_count": 0,
"link_to": "Sales Pipeline Analytics", "link_to": "Sales Pipeline Analytics",
"link_type": "Report", "link_type": "Report",
"onboard": 0, "onboard": 0,
@ -153,6 +155,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 1, "is_query_report": 1,
"label": "Opportunity Summary by Sales Stage", "label": "Opportunity Summary by Sales Stage",
"link_count": 0,
"link_to": "Opportunity Summary by Sales Stage", "link_to": "Opportunity Summary by Sales Stage",
"link_type": "Report", "link_type": "Report",
"onboard": 0, "onboard": 0,
@ -414,7 +417,7 @@
"type": "Link" "type": "Link"
} }
], ],
"modified": "2021-08-20 12:15:56.913092", "modified": "2022-01-13 17:53:17.509844",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "CRM", "name": "CRM",
@ -423,7 +426,7 @@
"public": 1, "public": 1,
"restrict_to_domain": "", "restrict_to_domain": "",
"roles": [], "roles": [],
"sequence_id": 7, "sequence_id": 7.0,
"shortcuts": [ "shortcuts": [
{ {
"color": "Blue", "color": "Blue",

View File

@ -1,18 +0,0 @@
[{
"account_name": "Debtors EUR",
"parent_account": "Accounts Receivable",
"account_type": "Receivable",
"account_currency": "EUR"
},
{
"account_name": "Creditors EUR",
"parent_account": "Accounts Payable",
"account_type": "Payable",
"account_currency": "EUR"
},
{
"account_name": "Paypal",
"parent_account": "Bank Accounts",
"account_type": "Bank",
"account_currency": "EUR"
}]

View File

@ -1,218 +0,0 @@
[
{
"address_line1": "254 Theotokopoulou Str.",
"address_type": "Office",
"city": "Larnaka",
"country": "Cyprus",
"links": [{"link_doctype": "Customer", "link_name": "Adaptas"}],
"phone": "23566775757"
},
{
"address_line1": "R Patr\u00e3o Caramelho 116",
"address_type": "Office",
"city": "Fajozes",
"country": "Portugal",
"links": [{"link_doctype": "Customer", "link_name": "Asian Fusion"}],
"phone": "23566775757"
},
{
"address_line1": "30 Fulford Road",
"address_type": "Office",
"city": "PENTRE-PIOD",
"country": "United Kingdom",
"links": [{"link_doctype": "Customer", "link_name": "Asian Junction"}],
"phone": "23566775757"
},
{
"address_line1": "Schoenebergerstrasse 13",
"address_type": "Office",
"city": "Raschau",
"country": "Germany",
"links": [{"link_doctype": "Customer", "link_name": "Big D Supermarkets"}],
"phone": "23566775757"
},
{
"address_line1": "Hoheluftchaussee 43",
"address_type": "Office",
"city": "Kieritzsch",
"country": "Germany",
"links": [{"link_doctype": "Customer", "link_name": "Buttrey Food & Drug"}],
"phone": "23566775757"
},
{
"address_line1": "R Cimo Vila 6",
"address_type": "Office",
"city": "Rebordosa",
"country": "Portugal",
"links": [{"link_doctype": "Customer", "link_name": "Chi-Chis"}],
"phone": "23566775757"
},
{
"address_line1": "R 5 Outubro 9",
"address_type": "Office",
"city": "Quinta Nova S\u00e3o Domingos",
"country": "Portugal",
"links": [{"link_doctype": "Customer", "link_name": "Choices"}],
"phone": "23566775757"
},
{
"address_line1": "Avenida Macambira 953",
"address_type": "Office",
"city": "Goi\u00e2nia",
"country": "Brazil",
"links": [{"link_doctype": "Customer", "link_name": "Consumers and Consumers Express"}],
"phone": "23566775757"
},
{
"address_line1": "2342 Goyeau Ave",
"address_type": "Office",
"city": "Windsor",
"country": "Canada",
"links": [{"link_doctype": "Customer", "link_name": "Crafts Canada"}],
"phone": "23566775757"
},
{
"address_line1": "Laukaantie 82",
"address_type": "Office",
"city": "KOKKOLA",
"country": "Finland",
"links": [{"link_doctype": "Customer", "link_name": "Endicott Shoes"}],
"phone": "23566775757"
},
{
"address_line1": "9 Brown Street",
"address_type": "Office",
"city": "PETERSHAM",
"country": "Australia",
"links": [{"link_doctype": "Customer", "link_name": "Fayva"}],
"phone": "23566775757"
},
{
"address_line1": "Via Donnalbina 41",
"address_type": "Office",
"city": "Cala Gonone",
"country": "Italy",
"links": [{"link_doctype": "Customer", "link_name": "Intelacard"}],
"phone": "23566775757"
},
{
"address_line1": "Liljerum Grenadj\u00e4rtorpet 69",
"address_type": "Office",
"city": "TOMTEBODA",
"country": "Sweden",
"links": [{"link_doctype": "Customer", "link_name": "Landskip Yard Care"}],
"phone": "23566775757"
},
{
"address_line1": "72 Bishopgate Street",
"address_type": "Office",
"city": "SEAHAM",
"country": "United Kingdom",
"links": [{"link_doctype": "Customer", "link_name": "Life Plan Counselling"}],
"phone": "23566775757"
},
{
"address_line1": "\u03a3\u03ba\u03b1\u03c6\u03af\u03b4\u03b9\u03b1 105",
"address_type": "Office",
"city": "\u03a0\u0391\u03a1\u0395\u039a\u039a\u039b\u0397\u03a3\u0399\u0391",
"country": "Cyprus",
"links": [{"link_doctype": "Customer", "link_name": "Mr Fables"}],
"phone": "23566775757"
},
{
"address_line1": "Mellemvej 7",
"address_type": "Office",
"city": "Aabybro",
"country": "Denmark",
"links": [{"link_doctype": "Customer", "link_name": "Nelson Brothers"}],
"phone": "23566775757"
},
{
"address_line1": "Plougg\u00e5rdsvej 98",
"address_type": "Office",
"city": "Karby",
"country": "Denmark",
"links": [{"link_doctype": "Customer", "link_name": "Netobill"}],
"phone": "23566775757"
},
{
"address_line1": "176 Michalakopoulou Street",
"address_type": "Office",
"city": "Agio Georgoudi",
"country": "Cyprus",
"phone": "23566775757",
"links": [{"link_doctype": "Supplier", "link_name": "Helios Air"}]
},
{
"address_line1": "Fibichova 1102",
"address_type": "Office",
"city": "Kokor\u00edn",
"country": "Czech Republic",
"phone": "23566775757",
"links": [{"link_doctype": "Supplier", "link_name": "Ks Merchandise"}]
},
{
"address_line1": "Zahradn\u00ed 888",
"address_type": "Office",
"city": "Cecht\u00edn",
"country": "Czech Republic",
"phone": "23566775757",
"links": [{"link_doctype": "Supplier", "link_name": "HomeBase"}]
},
{
"address_line1": "ul. Grochowska 94",
"address_type": "Office",
"city": "Warszawa",
"country": "Poland",
"phone": "23566775757",
"links": [{"link_doctype": "Supplier", "link_name": "Scott Ties"}]
},
{
"address_line1": "Norra Esplanaden 87",
"address_type": "Office",
"city": "HELSINKI",
"country": "Finland",
"phone": "23566775757",
"links": [{"link_doctype": "Supplier", "link_name": "Reliable Investments"}]
},
{
"address_line1": "2038 Fallon Drive",
"address_type": "Office",
"city": "Dresden",
"country": "Canada",
"phone": "23566775757",
"links": [{"link_doctype": "Supplier", "link_name": "Nan Duskin"}]
},
{
"address_line1": "77 cours Franklin Roosevelt",
"address_type": "Office",
"city": "MARSEILLE",
"country": "France",
"phone": "23566775757",
"links": [{"link_doctype": "Supplier", "link_name": "Rainbow Records"}]
},
{
"address_line1": "ul. Tuwima Juliana 85",
"address_type": "Office",
"city": "\u0141\u00f3d\u017a",
"country": "Poland",
"phone": "23566775757",
"links": [{"link_doctype": "Supplier", "link_name": "New World Realty"}]
},
{
"address_line1": "Gl. Sygehusvej 41",
"address_type": "Office",
"city": "Narsaq",
"country": "Greenland",
"phone": "23566775757",
"links": [{"link_doctype": "Supplier", "link_name": "Asiatic Solutions"}]
},
{
"address_line1": "Gosposka ulica 50",
"address_type": "Office",
"city": "Nova Gorica",
"country": "Slovenia",
"phone": "23566775757",
"links": [{"link_doctype": "Supplier", "link_name": "Eagle Hardware"}]
}
]

View File

@ -1,18 +0,0 @@
[
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Aptitude"
},
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Application"
},
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Understanding"
},
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Knowledge"
}
]

View File

@ -1,58 +0,0 @@
[
{
"asset_name": "Macbook Pro - 1",
"item_code": "Computer",
"gross_purchase_amount": 100000,
"asset_owner": "Company",
"available_for_use_date": "2017-01-02",
"location": "Main Location"
},
{
"asset_name": "Macbook Air - 1",
"item_code": "Computer",
"gross_purchase_amount": 60000,
"asset_owner": "Company",
"available_for_use_date": "2017-10-02",
"location": "Avg Location"
},
{
"asset_name": "Conferrence Table",
"item_code": "Table",
"gross_purchase_amount": 30000,
"asset_owner": "Company",
"available_for_use_date": "2018-10-02",
"location": "Zany Location"
},
{
"asset_name": "Lunch Table",
"item_code": "Table",
"gross_purchase_amount": 20000,
"asset_owner": "Company",
"available_for_use_date": "2018-06-02",
"location": "Fletcher Location"
},
{
"asset_name": "ERPNext",
"item_code": "ERP",
"gross_purchase_amount": 100000,
"asset_owner": "Company",
"available_for_use_date": "2018-09-02",
"location":"Main Location"
},
{
"asset_name": "Chair 1",
"item_code": "Chair",
"gross_purchase_amount": 10000,
"asset_owner": "Company",
"available_for_use_date": "2018-07-02",
"location": "Zany Location"
},
{
"asset_name": "Chair 2",
"item_code": "Chair",
"gross_purchase_amount": 10000,
"asset_owner": "Company",
"available_for_use_date": "2018-07-02",
"location": "Avg Location"
}
]

View File

@ -1,38 +0,0 @@
[
{
"asset_category_name": "Furnitures",
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 5,
"frequency_of_depreciation": 12,
"accounts": [{
"company_name": "Wind Power LLC",
"fixed_asset_account": "Furnitures and Fixtures - WPL",
"accumulated_depreciation_account": "Accumulated Depreciation - WPL",
"depreciation_expense_account": "Depreciation - WPL"
}]
},
{
"asset_category_name": "Electronic Equipments",
"depreciation_method": "Double Declining Balance",
"total_number_of_depreciations": 10,
"frequency_of_depreciation": 6,
"accounts": [{
"company_name": "Wind Power LLC",
"fixed_asset_account": "Electronic Equipments - WPL",
"accumulated_depreciation_account": "Accumulated Depreciation - WPL",
"depreciation_expense_account": "Depreciation - WPL"
}]
},
{
"asset_category_name": "Softwares",
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 10,
"frequency_of_depreciation": 12,
"accounts": [{
"company_name": "Wind Power LLC",
"fixed_asset_account": "Softwares - WPL",
"accumulated_depreciation_account": "Accumulated Depreciation - WPL",
"depreciation_expense_account": "Depreciation - WPL"
}]
}
]

View File

@ -1,180 +0,0 @@
[
{
"item": "Bearing Assembly",
"items": [
{
"item_code": "Base Bearing Plate",
"qty": 1.0,
"rate": 15.0
},
{
"item_code": "Bearing Block",
"qty": 1.0,
"rate": 10.0
},
{
"item_code": "Bearing Collar",
"qty": 2.0,
"rate": 20.0
},
{
"item_code": "Bearing Pipe",
"qty": 1.0,
"rate": 15.0
},
{
"item_code": "Upper Bearing Plate",
"qty": 1.0,
"rate": 50.0
}
]
},
{
"item": "Wind Mill A Series",
"items": [
{
"item_code": "Base Bearing Plate",
"qty": 1.0,
"rate": 15.0
},
{
"item_code": "Base Plate",
"qty": 1.0,
"rate": 20.0
},
{
"item_code": "Bearing Block",
"qty": 1.0,
"rate": 10.0
},
{
"item_code": "Bearing Pipe",
"qty": 1.0,
"rate": 15.0
},
{
"item_code": "External Disc",
"qty": 1.0,
"rate": 45.0
},
{
"item_code": "Shaft",
"qty": 1.0,
"rate": 30.0
},
{
"item_code": "Wing Sheet",
"qty": 4.0,
"rate": 22.0
}
]
},
{
"item": "Wind MIll C Series",
"items": [
{
"item_code": "Base Plate",
"qty": 2.0,
"rate": 20.0
},
{
"item_code": "Internal Disc",
"qty": 1.0,
"rate": 33.0
},
{
"item_code": "External Disc",
"qty": 1.0,
"rate": 45.0
},
{
"item_code": "Bearing Assembly",
"qty": 1.0,
"rate": 130.0
},
{
"item_code": "Wing Sheet",
"qty": 3.0,
"rate": 22.0
}
]
},
{
"item": "Wind Turbine-S",
"with_operations": 1,
"operations": [
{
"operation": "Prepare Frame",
"time_in_mins": 30.0,
"workstation": "Drilling Machine 1"
},
{
"operation": "Setup Fixtures",
"time_in_mins": 15.0,
"workstation": "Assembly Station 1"
},
{
"operation": "Assembly Operation",
"time_in_mins": 30.0,
"workstation": "Assembly Station 1"
},
{
"operation": "Wiring",
"time_in_mins": 20.0,
"workstation": "Assembly Station 1"
},
{
"operation": "Testing",
"time_in_mins": 10.0,
"workstation": "Packing and Testing Station"
},
{
"operation": "Packing",
"time_in_mins": 25.0,
"workstation": "Packing and Testing Station"
}
],
"items": [
{
"item_code": "Base Bearing Plate",
"qty": 1.0,
"rate": 15.0
},
{
"item_code": "Base Plate",
"qty": 1.0,
"rate": 20.0
},
{
"item_code": "Bearing Collar",
"qty": 1.0,
"rate": 20.0
},
{
"item_code": "Blade Rib",
"qty": 1.0,
"rate": 10.0
},
{
"item_code": "Shaft",
"qty": 1.0,
"rate": 30.0
},
{
"item_code": "Wing Sheet",
"qty": 2.0,
"rate": 22.0
}
]
},
{
"item": "Base Plate",
"items": [
{
"item_code": "Base Plate Un Painted",
"qty": 1.0,
"rate": 16.0
}
]
}
]

View File

@ -1,164 +0,0 @@
[
{
"email_id": "JanVaclavik@example.com",
"first_name": "January",
"last_name": "V\u00e1clav\u00edk",
"links": [{"link_doctype": "Customer", "link_name": "Adaptas"}]
},
{
"email_id": "ChidumagaTobeolisa@example.com",
"first_name": "Chidumaga",
"last_name": "Tobeolisa",
"links": [{"link_doctype": "Customer", "link_name": "Asian Fusion"}]
},
{
"email_id": "JanaKubanova@example.com",
"first_name": "Jana",
"last_name": "Kub\u00e1\u0148ov\u00e1",
"links": [{"link_doctype": "Customer", "link_name": "Asian Junction"}]
},
{
"email_id": "XuChaoXuan@example.com",
"first_name": "\u7d39\u8431",
"last_name": "\u4e8e",
"links": [{"link_doctype": "Customer", "link_name": "Big D Supermarkets"}]
},
{
"email_id": "OzlemVerwijmeren@example.com",
"first_name": "\u00d6zlem",
"last_name": "Verwijmeren",
"links": [{"link_doctype": "Customer", "link_name": "Buttrey Food & Drug"}]
},
{
"email_id": "HansRasmussen@example.com",
"first_name": "Hans",
"last_name": "Rasmussen",
"links": [{"link_doctype": "Customer", "link_name": "Chi-Chis"}]
},
{
"email_id": "SatomiShigeki@example.com",
"first_name": "Satomi",
"last_name": "Shigeki",
"links": [{"link_doctype": "Customer", "link_name": "Choices"}]
},
{
"email_id": "SimonVJessen@example.com",
"first_name": "Simon",
"last_name": "Jessen",
"links": [{"link_doctype": "Customer", "link_name": "Consumers and Consumers Express"}]
},
{
"email_id": "NeguaranShahsaah@example.com",
"first_name": "\u0646\u06af\u0627\u0631\u06cc\u0646",
"last_name": "\u0634\u0627\u0647 \u0633\u06cc\u0627\u0647",
"links": [{"link_doctype": "Customer", "link_name": "Crafts Canada"}]
},
{
"email_id": "Lom-AliBataev@example.com",
"first_name": "Lom-Ali",
"last_name": "Bataev",
"links": [{"link_doctype": "Customer", "link_name": "Endicott Shoes"}]
},
{
"email_id": "VanNgocTien@example.com",
"first_name": "Ti\u00ean",
"last_name": "V\u0103n",
"links": [{"link_doctype": "Customer", "link_name": "Fayva"}]
},
{
"email_id": "QuimeyOsorioRuelas@example.com",
"first_name": "Quimey",
"last_name": "Osorio",
"links": [{"link_doctype": "Customer", "link_name": "Intelacard"}]
},
{
"email_id": "EdgardaSalcedoRaya@example.com",
"first_name": "Edgarda",
"last_name": "Salcedo",
"links": [{"link_doctype": "Customer", "link_name": "Landskip Yard Care"}]
},
{
"email_id": "HafsteinnBjarnarsonar@example.com",
"first_name": "Hafsteinn",
"last_name": "Bjarnarsonar",
"links": [{"link_doctype": "Customer", "link_name": "Life Plan Counselling"}]
},
{
"email_id": "\u0434\u0430\u043d\u0438\u0438\u043b@example.com",
"first_name": "\u0414\u0430\u043d\u0438\u0438\u043b",
"last_name": "\u041a\u043e\u043d\u043e\u0432\u0430\u043b\u043e\u0432",
"links": [{"link_doctype": "Customer", "link_name": "Mr Fables"}]
},
{
"email_id": "SelmaMAndersen@example.com",
"first_name": "Selma",
"last_name": "Andersen",
"links": [{"link_doctype": "Customer", "link_name": "Nelson Brothers"}]
},
{
"email_id": "LadislavKolaja@example.com",
"first_name": "Ladislav",
"last_name": "Kolaja",
"links": [{"link_doctype": "Customer", "link_name": "Netobill"}]
},
{
"links": [{"link_doctype": "Supplier", "link_name": "Helios Air"}],
"email_id": "TewoldeAbaalom@example.com",
"first_name": "Tewolde",
"last_name": "Abaalom"
},
{
"links": [{"link_doctype": "Supplier", "link_name": "Ks Merchandise"}],
"email_id": "LeilaFernandesRodrigues@example.com",
"first_name": "Leila",
"last_name": "Rodrigues"
},
{
"links": [{"link_doctype": "Supplier", "link_name": "HomeBase"}],
"email_id": "DmitryBulgakov@example.com",
"first_name": "Dmitry",
"last_name": "Bulgakov"
},
{
"links": [{"link_doctype": "Supplier", "link_name": "Scott Ties"}],
"email_id": "HaiducWhitfoot@example.com",
"first_name": "Haiduc",
"last_name": "Whitfoot"
},
{
"links": [{"link_doctype": "Supplier", "link_name": "Reliable Investments"}],
"email_id": "SesseljaPetursdottir@example.com",
"first_name": "Sesselja",
"last_name": "P\u00e9tursd\u00f3ttir"
},
{
"links": [{"link_doctype": "Supplier", "link_name": "Nan Duskin"}],
"email_id": "HajdarPignar@example.com",
"first_name": "Hajdar",
"last_name": "Pignar"
},
{
"links": [{"link_doctype": "Supplier", "link_name": "Rainbow Records"}],
"email_id": "GustavaLorenzo@example.com",
"first_name": "Gustava",
"last_name": "Lorenzo"
},
{
"links": [{"link_doctype": "Supplier", "link_name": "New World Realty"}],
"email_id": "BethanyWood@example.com",
"first_name": "Bethany",
"last_name": "Wood"
},
{
"links": [{"link_doctype": "Supplier", "link_name": "Asiatic Solutions"}],
"email_id": "GlorianaBrownlock@example.com",
"first_name": "Gloriana",
"last_name": "Brownlock"
},
{
"links": [{"link_doctype": "Supplier", "link_name": "Eagle Hardware"}],
"email_id": "JensonFraser@gustr.com",
"first_name": "Jenson",
"last_name": "Fraser"
}
]

View File

@ -1,134 +0,0 @@
[
{
"doctype": "Course",
"course_name": "Communication Skiils",
"course_code": "BCA2040",
"department": "Information Technology"
},
{
"doctype": "Course",
"course_name": "Object Oriented Programing - C++",
"course_code": "BCA2030",
"department": "Information Technology"
},
{
"doctype": "Course",
"course_name": "Data Structures and Algorithm",
"course_code": "BCA2020",
"department": "Information Technology"
},
{
"doctype": "Course",
"course_name": "Operating System",
"course_code": "BCA2010",
"department": "Information Technology"
},
{
"doctype": "Course",
"course_name": "Digital Logic",
"course_code": "BCA1040",
"department": "Information Technology"
},
{
"doctype": "Course",
"course_name": "Basic Mathematics",
"course_code": "BCA1030",
"department": "Information Technology"
},
{
"doctype": "Course",
"course_name": "Programing in C",
"course_code": "BCA1020",
"department": "Information Technology"
},
{
"doctype": "Course",
"course_name": "Fundamentals of IT & Programing",
"course_code": "BCA1010",
"department": "Information Technology"
},
{
"doctype": "Course",
"course_name": "Microprocessor",
"course_code": "MCA4010",
"department": "Information Technology"
},
{
"doctype": "Course",
"course_name": "Probability and Statistics",
"course_code": "MCA4020",
"department": "Information Technology"
},
{
"doctype": "Course",
"course_name": "Programing in Java",
"course_code": "MCA4030",
"department": "Information Technology"
},
{
"doctype": "Course",
"course_name": "Communication Skills",
"course_code": "BBA 101",
"department": "Management Studies"
},
{
"doctype": "Course",
"course_name": "Organizational Behavior",
"course_code": "BBA 102",
"department": "Management Studies"
},
{
"doctype": "Course",
"course_name": "Business Environment",
"course_code": "BBA 103",
"department": "Management Studies"
},
{
"doctype": "Course",
"course_name": "Legal and Regulatory Framework",
"course_code": "BBA 301",
"department": "Management Studies"
},
{
"doctype": "Course",
"course_name": "Human Resource Management",
"course_code": "BBA 302",
"department": "Management Studies"
},
{
"doctype": "Course",
"course_name": "Advertising and Sales",
"course_code": "BBA 304",
"department": "Management Studies"
},
{
"doctype": "Course",
"course_name": "Entrepreneurship Management",
"course_code": "BBA 505",
"department": "Management Studies"
},
{
"doctype": "Course",
"course_name": "Visual Merchandising",
"course_code": "BBR 504",
"department": "Management Studies"
},
{
"doctype": "Course",
"course_name": "Warehouse Management",
"course_code": "BBR 505",
"department": "Management Studies"
},
{
"doctype": "Course",
"course_name": "Store Operations and Job Knowledge",
"course_code": "BBR 501",
"department": "Management Studies"
},
{
"doctype": "Course",
"course_name": "Management Development and Skills",
"course_code": "BBA 602",
"department": "Management Studies"
}
]

View File

@ -1,30 +0,0 @@
[
{
"doctype": "Department",
"department_name": "Information Technology"
},
{
"doctype": "Department",
"department_name": "Physics"
},
{
"doctype": "Department",
"department_name": "Chemistry"
},
{
"doctype": "Department",
"department_name": "Biology"
},
{
"doctype": "Department",
"department_name": "Commerce"
},
{
"doctype": "Department",
"department_name": "English"
},
{
"doctype": "Department",
"department_name": "Management Studies"
}
]

File diff suppressed because it is too large Load Diff

View File

@ -1,92 +0,0 @@
[
{
"date_of_birth": "1982-01-03",
"date_of_joining": "2001-10-10",
"employee_name": "Diana Prince",
"first_name": "Diana",
"last_name": "Prince",
"gender": "Female",
"user_id": "DianaPrince@example.com"
},
{
"date_of_birth": "1959-02-03",
"date_of_joining": "1976-09-16",
"employee_name": "Zatanna Zatara",
"gender": "Female",
"user_id": "ZatannaZatara@example.com",
"first_name": "Zatanna",
"last_name": "Zatara"
},
{
"date_of_birth": "1982-03-03",
"date_of_joining": "2000-06-16",
"employee_name": "Holly Granger",
"gender": "Female",
"user_id": "HollyGranger@example.com",
"first_name": "Holly",
"last_name": "Granger"
},
{
"date_of_birth": "1945-04-04",
"date_of_joining": "1969-07-01",
"employee_name": "Neptunia Aquaria",
"gender": "Female",
"user_id": "NeptuniaAquaria@example.com",
"first_name": "Neptunia",
"last_name": "Aquaria"
},
{
"date_of_birth": "1978-05-03",
"date_of_joining": "1999-12-24",
"employee_name": "Arthur Curry",
"gender": "Male",
"user_id": "ArthurCurry@example.com",
"first_name": "Arthur",
"last_name": "Curry"
},
{
"date_of_birth": "1964-06-03",
"date_of_joining": "1981-08-05",
"employee_name": "Thalia Al Ghul",
"gender": "Female",
"user_id": "ThaliaAlGhul@example.com",
"first_name": "Thalia",
"last_name": "Al Ghul"
},
{
"date_of_birth": "1982-07-03",
"date_of_joining": "2006-06-10",
"employee_name": "Maxwell Lord",
"gender": "Male",
"user_id": "MaxwellLord@example.com",
"first_name": "Maxwell",
"last_name": "Lord"
},
{
"date_of_birth": "1969-08-03",
"date_of_joining": "1993-10-21",
"employee_name": "Grace Choi",
"gender": "Female",
"user_id": "GraceChoi@example.com",
"first_name": "Grace",
"last_name": "Choi"
},
{
"date_of_birth": "1982-09-03",
"date_of_joining": "2005-09-06",
"employee_name": "Vandal Savage",
"gender": "Male",
"user_id": "VandalSavage@example.com",
"first_name": "Vandal",
"last_name": "Savage"
},
{
"date_of_birth": "1985-10-03",
"date_of_joining": "2007-12-25",
"employee_name": "Caitlin Snow",
"gender": "Female",
"user_id": "CaitlinSnow@example.com",
"first_name": "Caitlin",
"last_name": "Snow"
}
]

View File

@ -1,17 +0,0 @@
[
{
"doctype": "Grading Scale",
"grading_scale_name": "Standard Grading",
"description": "Standard Grading Scale",
"intervals": [
{"threshold": 100.0, "grade_code": "A", "grade_description": "Excellent"},
{"threshold": 89.9, "grade_code": "B+", "grade_description": "Close to Excellence"},
{"threshold": 80.0, "grade_code": "B", "grade_description": "Good"},
{"threshold": 69.9, "grade_code": "C+", "grade_description": "Almost Good"},
{"threshold": 60.0, "grade_code": "C", "grade_description": "Average"},
{"threshold": 50.0, "grade_code": "D+", "grade_description": "Have to Work"},
{"threshold": 40.0, "grade_code": "D", "grade_description": "Not met Baseline Expectations"},
{"threshold": 0.0, "grade_code": "F", "grade_description": "Have to work a lot"}
]
}
]

View File

@ -1,128 +0,0 @@
[
{
"doctype": "Instructor",
"instructor_name": "Eddie Jessup",
"naming_series": "INS/",
"department": "Information Technology"
},
{
"doctype": "Instructor",
"instructor_name": "William Dyer",
"naming_series": "INS/",
"department": "Information Technology"
},
{
"doctype": "Instructor",
"instructor_name": "Alastor Moody",
"naming_series": "INS/",
"department": "Information Technology"
},
{
"doctype": "Instructor",
"instructor_name": "Charles Xavier",
"naming_series": "INS/",
"department": "Information Technology"
},
{
"doctype": "Instructor",
"instructor_name": "Cuthbert Calculus",
"naming_series": "INS/",
"department": "Information Technology"
},
{
"doctype": "Instructor",
"instructor_name": "Reed Richards",
"naming_series": "INS/",
"department": "Information Technology"
},
{
"doctype": "Instructor",
"instructor_name": "Urban Chronotis",
"naming_series": "INS/",
"department": "Physics"
},
{
"doctype": "Instructor",
"instructor_name": "River Song",
"naming_series": "INS/",
"department": "Physics"
},
{
"doctype": "Instructor",
"instructor_name": "Yana",
"naming_series": "INS/",
"department": "Physics"
},
{
"doctype": "Instructor",
"instructor_name": "Neil Lasrado",
"naming_series": "INS/",
"department": "Information Technology"
},
{
"doctype": "Instructor",
"instructor_name": "Deepshi Garg",
"naming_series": "INS/",
"department": "Chemistry"
},
{
"doctype": "Instructor",
"instructor_name": "Shubham Saxena",
"naming_series": "INS/",
"department": "Physics"
},
{
"doctype": "Instructor",
"instructor_name": "Rushabh Mehta",
"naming_series": "INS/",
"department": "Information Technology"
},
{
"doctype": "Instructor",
"instructor_name": "Umari Syed",
"naming_series": "INS/",
"department": "Chemistry"
},
{
"doctype": "Instructor",
"instructor_name": "Aman Singh",
"naming_series": "INS/",
"department": "Physics"
},
{
"doctype": "Instructor",
"instructor_name": "Nabin",
"naming_series": "INS/",
"department": "Chemistry"
},
{
"doctype": "Instructor",
"instructor_name": "Kanchan Chauhan",
"naming_series": "INS/",
"department": "Information Technology"
},
{
"doctype": "Instructor",
"instructor_name": "Valmik Jangla",
"naming_series": "INS/",
"department": "Chemistry"
},
{
"doctype": "Instructor",
"instructor_name": "Amit Jain",
"naming_series": "INS/",
"department": "Physics"
},
{
"doctype": "Instructor",
"instructor_name": "Shreyas P",
"naming_series": "INS/",
"department": "Chemistry"
},
{
"doctype": "Instructor",
"instructor_name": "Rohit",
"naming_series": "INS/",
"department": "Information Technology"
}
]

View File

@ -1,493 +0,0 @@
[
{
"item_defaults": [
{
"default_supplier": "Asiatic Solutions",
"default_warehouse": "Stores"
}
],
"description": "For Upper Bearing",
"image": "/assets/erpnext_demo/images/disc.png",
"item_code": "Disc Collars",
"item_group": "Raw Material",
"item_name": "Disc Collars"
},
{
"item_defaults": [
{
"default_supplier": "Nan Duskin",
"default_warehouse": "Stores"
}
],
"description": "CAST IRON, MCMASTER PART NO. 3710T13",
"image": "/assets/erpnext_demo/images/bearing.jpg",
"item_code": "Bearing Block",
"item_group": "Raw Material",
"item_name": "Bearing Block"
},
{
"item_defaults": [
{
"default_supplier": null,
"default_warehouse": "Finished Goods"
}
],
"description": "Wind Mill C Series for Commercial Use 18ft",
"image": "/assets/erpnext_demo/images/wind-turbine-2.png",
"item_code": "Wind MIll C Series",
"item_group": "Products",
"item_name": "Wind MIll C Series"
},
{
"item_defaults": [
{
"default_supplier": null,
"default_warehouse": "Finished Goods"
}
],
"description": "Wind Mill A Series for Home Use 9ft",
"image": "/assets/erpnext_demo/images/wind-turbine.png",
"item_code": "Wind Mill A Series",
"item_group": "Products",
"item_name": "Wind Mill A Series"
},
{
"item_defaults": [
{
"default_supplier": null,
"default_warehouse": "Finished Goods"
}
],
"description": "Small Wind Turbine for Home Use\n\n\n<!-- html -->",
"image": "/assets/erpnext_demo/images/wind-turbine-1.jpg",
"item_code": "Wind Turbine",
"item_group": "Products",
"item_name": "Wind Turbine",
"has_variants": 1,
"has_serial_no": 1,
"attributes": [
{
"attribute": "Size"
}
]
},
{
"item_defaults": [
{
"default_supplier": "HomeBase",
"default_warehouse": "Stores"
}
],
"description": "1.5 in. Diameter x 36 in. Mild Steel Tubing",
"image": null,
"item_code": "Bearing Pipe",
"item_group": "Raw Material",
"item_name": "Bearing Pipe"
},
{
"item_defaults": [
{
"default_supplier": "New World Realty",
"default_warehouse": "Stores"
}
],
"description": "1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet",
"image": null,
"item_code": "Wing Sheet",
"item_group": "Raw Material",
"item_name": "Wing Sheet"
},
{
"item_defaults": [
{
"default_supplier": "Eagle Hardware",
"default_warehouse": "Stores"
}
],
"description": "3/16 in. x 6 in. x 6 in. Low Carbon Steel Plate",
"image": null,
"item_code": "Upper Bearing Plate",
"item_group": "Raw Material",
"item_name": "Upper Bearing Plate"
},
{
"item_defaults": [
{
"default_supplier": "Asiatic Solutions",
"default_warehouse": "Stores"
}
],
"description": "Bearing Assembly",
"image": null,
"item_code": "Bearing Assembly",
"item_group": "Sub Assemblies",
"item_name": "Bearing Assembly"
},
{
"item_defaults": [
{
"default_supplier": "HomeBase",
"default_warehouse": "Stores"
}
],
"description": "3/4 in. x 2 ft. x 4 ft. Pine Plywood",
"image": null,
"item_code": "Base Plate",
"item_group": "Raw Material",
"item_name": "Base Plate",
"is_sub_contracted_item": 1
},
{
"item_defaults": [
{
"default_supplier": "Scott Ties",
"default_warehouse": "Stores"
}
],
"description": "N/A",
"image": null,
"item_code": "Stand",
"item_group": "Raw Material",
"item_name": "Stand"
},
{
"item_defaults": [
{
"default_supplier": "Eagle Hardware",
"default_warehouse": "Stores"
}
],
"description": "1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar",
"image": null,
"item_code": "Bearing Collar",
"item_group": "Raw Material",
"item_name": "Bearing Collar"
},
{
"item_defaults": [
{
"default_supplier": "Eagle Hardware",
"default_warehouse": "Stores"
}
],
"description": "1/4 in. x 6 in. x 6 in. Mild Steel Plate",
"image": null,
"item_code": "Base Bearing Plate",
"item_group": "Raw Material",
"item_name": "Base Bearing Plate"
},
{
"item_defaults": [
{
"default_supplier": "HomeBase",
"default_warehouse": "Stores"
}
],
"description": "15/32 in. x 4 ft. x 8 ft. 3-Ply Rtd Sheathing",
"image": null,
"item_code": "External Disc",
"item_group": "Raw Material",
"item_name": "External Disc"
},
{
"item_defaults": [
{
"default_supplier": "Eagle Hardware",
"default_warehouse": "Stores"
}
],
"description": "1.25 in. Diameter x 6 ft. Mild Steel Tubing",
"image": null,
"item_code": "Shaft",
"item_group": "Raw Material",
"item_name": "Shaft"
},
{
"item_defaults": [
{
"default_supplier": "Ks Merchandise",
"default_warehouse": "Stores"
}
],
"description": "1/2 in. x 2 ft. x 4 ft. Pine Plywood",
"image": null,
"item_code": "Blade Rib",
"item_group": "Raw Material",
"item_name": "Blade Rib"
},
{
"item_defaults": [
{
"default_supplier": "HomeBase",
"default_warehouse": "Stores"
}
],
"description": "For Bearing Collar",
"image": null,
"item_code": "Internal Disc",
"item_group": "Raw Material",
"item_name": "Internal Disc"
},
{
"item_defaults": [
{
"default_supplier": null,
"default_warehouse": "Finished Goods"
}
],
"description": "Small Wind Turbine for Home Use\n\n\n<!-- html -->\n<p>Size: Small</p>",
"image": "/assets/erpnext_demo/images/wind-turbine-1.jpg",
"item_code": "Wind Turbine-S",
"item_group": "Products",
"item_name": "Wind Turbine-S",
"variant_of": "Wind Turbine",
"valuation_rate": 300,
"attributes": [
{
"attribute": "Size",
"attribute_value": "Small"
}
]
},
{
"item_defaults": [
{
"default_supplier": null,
"default_warehouse": "Finished Goods"
}
],
"description": "Small Wind Turbine for Home Use\n\n\n<!-- html -->\n<p>Size: Medium</p>",
"image": "/assets/erpnext_demo/images/wind-turbine-1.jpg",
"item_code": "Wind Turbine-M",
"item_group": "Products",
"item_name": "Wind Turbine-M",
"variant_of": "Wind Turbine",
"valuation_rate": 300,
"attributes": [
{
"attribute": "Size",
"attribute_value": "Medium"
}
]
},
{
"item_defaults": [
{
"default_supplier": null,
"default_warehouse": "Finished Goods"
}
],
"description": "Small Wind Turbine for Home Use\n\n\n<!-- html -->\n<p>Size: Large</p>",
"image": "/assets/erpnext_demo/images/wind-turbine-1.jpg",
"item_code": "Wind Turbine-L",
"item_group": "Products",
"item_name": "Wind Turbine-L",
"variant_of": "Wind Turbine",
"valuation_rate": 300,
"attributes": [
{
"attribute": "Size",
"attribute_value": "Large"
}
]
},
{
"is_stock_item": 0,
"description": "Wind Mill A Series with Spare Bearing",
"item_code": "Wind Mill A Series with Spare Bearing",
"item_group": "Products",
"item_name": "Wind Mill A Series with Spare Bearing"
},
{
"item_defaults": [
{
"default_supplier": "HomeBase",
"default_warehouse": "Stores"
}
],
"description": "3/4 in. x 2 ft. x 4 ft. Pine Plywood",
"image": null,
"item_code": "Base Plate Un Painted",
"item_group": "Raw Material",
"item_name": "Base Plate Un Painted"
},
{
"is_fixed_asset": 1,
"asset_category": "Furnitures",
"is_stock_item": 0,
"description": "Table",
"item_code": "Table",
"item_name": "Table",
"item_group": "Products"
},
{
"is_fixed_asset": 1,
"asset_category": "Furnitures",
"is_stock_item": 0,
"description": "Chair",
"item_code": "Chair",
"item_name": "Chair",
"item_group": "Products"
},
{
"is_fixed_asset": 1,
"asset_category": "Electronic Equipments",
"is_stock_item": 0,
"description": "Computer",
"item_code": "Computer",
"item_name": "Computer",
"item_group": "Products"
},
{
"is_fixed_asset": 1,
"asset_category": "Electronic Equipments",
"is_stock_item": 0,
"description": "Mobile",
"item_code": "Mobile",
"item_name": "Mobile",
"item_group": "Products"
},
{
"is_fixed_asset": 1,
"asset_category": "Softwares",
"is_stock_item": 0,
"description": "ERP",
"item_code": "ERP",
"item_name": "ERP",
"item_group": "All Item Groups"
},
{
"is_fixed_asset": 1,
"asset_category": "Softwares",
"is_stock_item": 0,
"description": "Autocad",
"item_code": "Autocad",
"item_name": "Autocad",
"item_group": "All Item Groups"
},
{
"is_stock_item": 1,
"has_batch_no": 1,
"create_new_batch": 1,
"valuation_rate": 200,
"item_defaults": [
{
"default_warehouse": "Stores"
}
],
"description": "Corrugated Box",
"item_code": "Corrugated Box",
"item_name": "Corrugated Box",
"item_group": "All Item Groups"
},
{
"item_defaults": [
{
"default_warehouse": "Finished Goods"
}
],
"is_stock_item": 1,
"description": "OnePlus 6",
"item_code": "OnePlus 6",
"item_name": "OnePlus 6",
"item_group": "Products",
"domain": "Retail"
},
{
"item_defaults": [
{
"default_warehouse": "Finished Goods"
}
],
"is_stock_item": 1,
"description": "OnePlus 6T",
"item_code": "OnePlus 6T",
"item_name": "OnePlus 6T",
"item_group": "Products",
"domain": "Retail"
},
{
"item_defaults": [
{
"default_warehouse": "Finished Goods"
}
],
"is_stock_item": 1,
"description": "Xiaomi Poco F1",
"item_code": "Xiaomi Poco F1",
"item_name": "Xiaomi Poco F1",
"item_group": "Products",
"domain": "Retail"
},
{
"item_defaults": [
{
"default_warehouse": "Finished Goods"
}
],
"is_stock_item": 1,
"description": "Iphone XS",
"item_code": "Iphone XS",
"item_name": "Iphone XS",
"item_group": "Products",
"domain": "Retail"
},
{
"item_defaults": [
{
"default_warehouse": "Finished Goods"
}
],
"is_stock_item": 1,
"description": "Samsung Galaxy S9",
"item_code": "Samsung Galaxy S9",
"item_name": "Samsung Galaxy S9",
"item_group": "Products",
"domain": "Retail"
},
{
"item_defaults": [
{
"default_warehouse": "Finished Goods"
}
],
"is_stock_item": 1,
"description": "Sony Bluetooth Headphone",
"item_code": "Sony Bluetooth Headphone",
"item_name": "Sony Bluetooth Headphone",
"item_group": "Products",
"domain": "Retail"
},
{
"is_stock_item": 0,
"description": "Samsung Phone Repair",
"item_code": "Samsung Phone Repair",
"item_name": "Samsung Phone Repair",
"item_group": "Services",
"domain": "Retail"
},
{
"is_stock_item": 0,
"description": "OnePlus Phone Repair",
"item_code": "OnePlus Phone Repair",
"item_name": "OnePlus Phone Repair",
"item_group": "Services",
"domain": "Retail"
},
{
"is_stock_item": 0,
"description": "Xiaomi Phone Repair",
"item_code": "Xiaomi Phone Repair",
"item_name": "Xiaomi Phone Repair",
"item_group": "Services",
"domain": "Retail"
},
{
"is_stock_item": 0,
"description": "Apple Phone Repair",
"item_code": "Apple Phone Repair",
"item_name": "Apple Phone Repair",
"item_group": "Services",
"domain": "Retail"
}
]

View File

@ -1,137 +0,0 @@
[
{
"default_supplier": "Asiatic Solutions",
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"item_code": "Books",
"item_group": "Raw Material",
"item_name": "Books"
},
{
"default_supplier": "HomeBase",
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"item_code": "Pencil",
"item_group": "Raw Material",
"item_name": "Pencil"
},
{
"default_supplier": "New World Realty",
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"item_code": "Tables",
"item_group": "Raw Material",
"item_name": "Tables"
},
{
"default_supplier": "Eagle Hardware",
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"item_code": "Chair",
"item_group": "Raw Material",
"item_name": "Chair"
},
{
"default_supplier": "Asiatic Solutions",
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"item_code": "Black Board",
"item_group": "Sub Assemblies",
"item_name": "Black Board"
},
{
"default_supplier": "HomeBase",
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"item_code": "Chalk",
"item_group": "Raw Material",
"item_name": "Chalk"
},
{
"default_supplier": "HomeBase",
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"item_code": "Notepad",
"item_group": "Raw Material",
"item_name": "Notepad"
},
{
"default_supplier": "Ks Merchandise",
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"item_code": "Uniform",
"item_group": "Raw Material",
"item_name": "Uniform"
},
{
"is_stock_item": 0,
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"description": "Computer",
"item_code": "Computer",
"item_name": "Computer",
"item_group": "Products"
},
{
"is_stock_item": 0,
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"description": "Mobile",
"item_code": "Mobile",
"item_name": "Mobile",
"item_group": "Products"
},
{
"is_stock_item": 0,
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"description": "ERP",
"item_code": "ERP",
"item_name": "ERP",
"item_group": "All Item Groups"
},
{
"is_stock_item": 0,
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"description": "Autocad",
"item_code": "Autocad",
"item_name": "Autocad",
"item_group": "All Item Groups"
},
{
"item_defaults": [{
"default_warehouse": "Stores",
"company": "Whitmore College"
}],
"item_code": "Service",
"item_group": "Services",
"item_name": "Service",
"has_variants": 0,
"is_stock_item": 0
}
]

View File

@ -1,127 +0,0 @@
[
{
"company_name": "Zany Brainy",
"email_id": "MartLakeman@example.com",
"lead_name": "Mart Lakeman"
},
{
"company_name": "Patterson-Fletcher",
"email_id": "SagaLundqvist@example.com",
"lead_name": "Saga Lundqvist"
},
{
"company_name": "Griff's Hamburgers",
"email_id": "AdnaSjoberg@example.com",
"lead_name": "Adna Sj\u00f6berg"
},
{
"company_name": "Rhodes Furniture",
"email_id": "IdaDSvendsen@example.com",
"lead_name": "Ida Svendsen"
},
{
"company_name": "Burger Chef",
"email_id": "EmppuHameenniemi@example.com",
"lead_name": "Emppu H\u00e4meenniemi"
},
{
"company_name": "Stratabiz",
"email_id": "EugenioPisano@example.com",
"lead_name": "Eugenio Pisano"
},
{
"company_name": "Home Quarters Warehouse",
"email_id": "SemharHagos@example.com",
"lead_name": "Semhar Hagos"
},
{
"company_name": "Enviro Architectural Designs",
"email_id": "BranimiraIvankovic@example.com",
"lead_name": "Branimira Ivankovi\u0107"
},
{
"company_name": "Ideal Garden Management",
"email_id": "ShellyLFields@example.com",
"lead_name": "Shelly Fields"
},
{
"company_name": "Listen Up",
"email_id": "LeoMikulic@example.com",
"lead_name": "Leo Mikuli\u0107"
},
{
"company_name": "I. Magnin",
"email_id": "DenisaJarosova@example.com",
"lead_name": "Denisa Jaro\u0161ov\u00e1"
},
{
"company_name": "First Rate Choice",
"email_id": "JanekRutkowski@example.com",
"lead_name": "Janek Rutkowski"
},
{
"company_name": "Multi Tech Development",
"email_id": "mm@example.com",
"lead_name": "\u7f8e\u6708 \u5b87\u85e4"
},
{
"company_name": "National Auto Parts",
"email_id": "dd@example.com",
"lead_name": "\u0414\u0430\u043d\u0438\u0438\u043b \u0410\u0444\u0430\u043d\u0430\u0441\u044c\u0435\u0432"
},
{
"company_name": "Integra Investment Plan",
"email_id": "ZorislavPetkovic@example.com",
"lead_name": "Zorislav Petkovi\u0107"
},
{
"company_name": "The Lawn Guru",
"email_id": "NanaoNiwa@example.com",
"lead_name": "Nanao Niwa"
},
{
"company_name": "Buena Vista Realty Service",
"email_id": "HreiarJorundsson@example.com",
"lead_name": "Hrei\u00f0ar J\u00f6rundsson"
},
{
"company_name": "Bountiful Harvest Health Food Store",
"email_id": "ChuThiBichLai@example.com",
"lead_name": "Lai Chu"
},
{
"company_name": "P. Samuels Men's Clothiers",
"email_id": "VictorAksakov@example.com",
"lead_name": "Victor Aksakov"
},
{
"company_name": "Vinyl Fever",
"email_id": "SaidalimBisliev@example.com",
"lead_name": "Saidalim Bisliev"
},
{
"company_name": "Garden Master",
"email_id": "TotteJakobsson@example.com",
"lead_name": "Totte Jakobsson"
},
{
"company_name": "Big Apple",
"email_id": "NanaArmasRobles@example.com",
"lead_name": "Nan\u00e1 Armas"
},
{
"company_name": "Monk House Sales",
"email_id": "WalerianDuda@example.com",
"lead_name": "Walerian Duda"
},
{
"company_name": "ManCharm",
"email_id": "Moarimikashi@example.com",
"lead_name": "Moarimikashi"
},
{
"company_name": "Custom Lawn Care",
"email_id": "DobromilDabrowski@example.com",
"lead_name": "Dobromi\u0142 D\u0105browski"
}
]

View File

@ -1,22 +0,0 @@
[
{
"location_name": "Main Location",
"latitude": 40.0,
"longitude": 20.0
},
{
"location_name": "Avg Location",
"latitude": 63.0,
"longitude": 99.3
},
{
"location_name": "Zany Location",
"latitude": 47.5,
"longitude": 10.0
},
{
"location_name": "Fletcher Location",
"latitude": 100.90,
"longitude": 80
}
]

View File

@ -1,32 +0,0 @@
[
{
"description": "Setup Fixtures for Assembly",
"name": "Setup Fixtures",
"workstation": "Assembly Station 1"
},
{
"description": "Assemble Unit as per Standard Operating Procedures",
"name": "Assembly Operation",
"workstation": "Assembly Station 1"
},
{
"description": "Final Testing Checklist",
"name": "Testing",
"workstation": "Packing and Testing Station"
},
{
"description": "Final Packing and add Instructions",
"name": "Packing",
"workstation": "Packing and Testing Station"
},
{
"description": "Prepare frame for assembly",
"name": "Prepare Frame",
"workstation": "Drilling Machine 1"
},
{
"description": "Connect wires",
"name": "Wiring",
"workstation": "Assembly Station 1"
}
]

View File

@ -1,27 +0,0 @@
[
{
"patient_name": "lila",
"gender": "Female"
},
{
"patient_name": "charline",
"gender": "Female"
},
{
"patient_name": "soren",
"last_name": "le gall",
"gender": "Male"
},
{
"patient_name": "fanny",
"gender": "Female"
},
{
"patient_name": "julie",
"gender": "Female"
},
{
"patient_name": "louka",
"gender": "Male"
}
]

View File

@ -1,17 +0,0 @@
[
{
"doctype": "Healthcare Practitioner",
"first_name": "Eddie Jessup",
"department": "Pathology"
},
{
"doctype": "Healthcare Practitioner",
"first_name": "Deepshi Garg",
"department": "ENT"
},
{
"doctype": "Healthcare Practitioner",
"first_name": "Amit Jain",
"department": "Microbiology"
}
]

View File

@ -1,46 +0,0 @@
[
{
"doctype": "Program",
"name": "MCA",
"program_name": "Masters of Computer Applications",
"program_code": "MCA",
"department": "Information Technology",
"courses": [
{ "course": "MCA4010" },
{ "course": "MCA4020" },
{ "course": "MCA4030" }
]
},
{
"doctype": "Program",
"name": "BCA",
"program_name": "Bachelor of Computer Applications",
"program_code": "BCA",
"department": "Information Technology",
"courses": [
{ "course": "BCA2030" },
{ "course": "BCA1030" },
{ "course": "BCA2020" },
{ "course": "BCA1040" },
{ "course": "BCA1010" },
{ "course": "BCA2010" },
{ "course": "BCA1020" }
]
},
{
"doctype": "Program",
"name": "BBA",
"program_name": "Bachelor of Business Administration",
"program_code": "BBA",
"department": "Management Studies",
"courses": [
{ "course": "BBA 101" },
{ "course": "BBA 102" },
{ "course": "BBA 103" },
{ "course": "BBA 301" },
{ "course": "BBA 302" },
{ "course": "BBA 304" },
{ "course": "BBA 505" }
]
}
]

File diff suppressed because it is too large Load Diff

View File

@ -1,122 +0,0 @@
[
{
"doctype": "Room",
"room_name": "Lecture Hall 1",
"room_number": "101",
"seating_capacity": 80
},
{
"doctype": "Room",
"room_name": "Lecture Hall 2",
"room_number": "102",
"seating_capacity": 80
},
{
"doctype": "Room",
"room_name": "Lecture Hall 3",
"room_number": "103",
"seating_capacity": 80
},
{
"doctype": "Room",
"room_name": "Lecture Hall 4",
"room_number": "104",
"seating_capacity": 80
},
{
"doctype": "Room",
"room_name": "Lecture Hall 4",
"room_number": "104",
"seating_capacity": 80
},
{
"doctype": "Room",
"room_name": "Lecture Hall 5",
"room_number": "201",
"seating_capacity": 120
},
{
"doctype": "Room",
"room_name": "Lecture Hall 6",
"room_number": "202",
"seating_capacity": 120
},
{
"doctype": "Room",
"room_name": "Lecture Hall 7",
"room_number": "203",
"seating_capacity": 120
},
{
"doctype": "Room",
"room_name": "Computer Lab 1",
"room_number": "301",
"seating_capacity": 40
},
{
"doctype": "Room",
"room_name": "Computer Lab 2",
"room_number": "302",
"seating_capacity": 60
},
{
"doctype": "Room",
"room_name": "Seminar Hall 1",
"room_number": "303",
"seating_capacity": 240
},
{
"doctype": "Room",
"room_name": "Auditorium",
"room_number": "400",
"seating_capacity": 450
},
{
"doctype": "Room",
"room_name": "Exam hall 1",
"room_number": "560",
"seating_capacity": 70
},
{
"doctype": "Room",
"room_name": "Exam hall 2",
"room_number": "561",
"seating_capacity": 70
},
{
"doctype": "Room",
"room_name": "Exam hall 2",
"room_number": "562",
"seating_capacity": 70
},
{
"doctype": "Room",
"room_name": "Exam hall 3",
"room_number": "563",
"seating_capacity": 70
},
{
"doctype": "Room",
"room_name": "Exam hall 4",
"room_number": "564",
"seating_capacity": 70
},
{
"doctype": "Room",
"room_name": "Exam hall 5",
"room_number": "565",
"seating_capacity": 70
},
{
"doctype": "Room",
"room_name": "Exam hall 6",
"room_number": "566",
"seating_capacity": 70
},
{
"doctype": "Room",
"room_name": "Exam hall 7",
"room_number": "567",
"seating_capacity": 70
}
]

View File

@ -1,10 +0,0 @@
[
{
"doctype": "Student Batch Name",
"batch_name": "Section-A"
},
{
"doctype": "Student Batch Name",
"batch_name": "Section-B"
}
]

View File

@ -1,112 +0,0 @@
[
{
"email": "test_demo@erpnext.com",
"first_name": "Test",
"last_name": "User"
},
{
"email": "DianaPrince@example.com",
"first_name": "Diana",
"last_name": "Prince"
},
{
"email": "ZatannaZatara@example.com",
"first_name": "Zatanna",
"last_name": "Zatara"
},
{
"email": "HollyGranger@example.com",
"first_name": "Holly",
"last_name": "Granger"
},
{
"email": "NeptuniaAquaria@example.com",
"first_name": "Neptunia",
"last_name": "Aquaria"
},
{
"email": "ArthurCurry@example.com",
"first_name": "Arthur",
"last_name": "Curry"
},
{
"email": "ThaliaAlGhul@example.com",
"first_name": "Thalia",
"last_name": "Al Ghul"
},
{
"email": "MaxwellLord@example.com",
"first_name": "Maxwell",
"last_name": "Lord"
},
{
"email": "GraceChoi@example.com",
"first_name": "Grace",
"last_name": "Choi"
},
{
"email": "VandalSavage@example.com",
"first_name": "Vandal",
"last_name": "Savage"
},
{
"email": "CaitlinSnow@example.com",
"first_name": "Caitlin",
"last_name": "Snow"
},
{
"email": "RipHunter@example.com",
"first_name": "Rip",
"last_name": "Hunter"
},
{
"email": "NicholasFury@example.com",
"first_name": "Nicholas",
"last_name": "Fury"
},
{
"email": "PeterParker@example.com",
"first_name": "Peter",
"last_name": "Parker"
},
{
"email": "JohnConstantine@example.com",
"first_name": "John",
"last_name": "Constantine"
},
{
"email": "HalJordan@example.com",
"first_name": "Hal",
"last_name": "Jordan"
},
{
"email": "VictorStone@example.com",
"first_name": "Victor",
"last_name": "Stone"
},
{
"email": "BruceWayne@example.com",
"first_name": "Bruce",
"last_name": "Wayne"
},
{
"email": "ClarkKent@example.com",
"first_name": "Clark",
"last_name": "Kent"
},
{
"email": "BarryAllen@example.com",
"first_name": "Barry",
"last_name": "Allen"
},
{
"email": "KaraZorEl@example.com",
"first_name": "Kara",
"last_name": "Zor El"
},
{
"email": "demo@erpnext.com",
"first_name": "Demo",
"last_name": "User"
}
]

View File

@ -1,97 +0,0 @@
import sys
import frappe
import frappe.utils
import erpnext
from erpnext.demo.setup import education, manufacture, retail, setup_data
from erpnext.demo.user import accounts
from erpnext.demo.user import education as edu
from erpnext.demo.user import fixed_asset, hr, manufacturing, projects, purchase, sales, stock
"""
Make a demo
1. Start with a fresh account
bench --site demo.erpnext.dev reinstall
2. Install Demo
bench --site demo.erpnext.dev execute erpnext.demo.demo.make
3. If Demo breaks, to continue
bench --site demo.erpnext.dev execute erpnext.demo.demo.simulate
"""
def make(domain='Manufacturing', days=100):
frappe.flags.domain = domain
frappe.flags.mute_emails = True
setup_data.setup(domain)
if domain== 'Manufacturing':
manufacture.setup_data()
elif domain == "Retail":
retail.setup_data()
elif domain== 'Education':
education.setup_data()
site = frappe.local.site
frappe.destroy()
frappe.init(site)
frappe.connect()
simulate(domain, days)
def simulate(domain='Manufacturing', days=100):
runs_for = frappe.flags.runs_for or days
frappe.flags.company = erpnext.get_default_company()
frappe.flags.mute_emails = True
if not frappe.flags.start_date:
# start date = 100 days back
frappe.flags.start_date = frappe.utils.add_days(frappe.utils.nowdate(),
-1 * runs_for)
current_date = frappe.utils.getdate(frappe.flags.start_date)
# continue?
demo_last_date = frappe.db.get_global('demo_last_date')
if demo_last_date:
current_date = frappe.utils.add_days(frappe.utils.getdate(demo_last_date), 1)
# run till today
if not runs_for:
runs_for = frappe.utils.date_diff(frappe.utils.nowdate(), current_date)
# runs_for = 100
fixed_asset.work()
for i in range(runs_for):
sys.stdout.write("\rSimulating {0}: Day {1}".format(
current_date.strftime("%Y-%m-%d"), i))
sys.stdout.flush()
frappe.flags.current_date = current_date
if current_date.weekday() in (5, 6):
current_date = frappe.utils.add_days(current_date, 1)
continue
try:
hr.work()
purchase.work()
stock.work()
accounts.work()
projects.run_projects(current_date)
sales.work(domain)
# run_messages()
if domain=='Manufacturing':
manufacturing.work()
elif domain=='Education':
edu.work()
except Exception:
frappe.db.set_global('demo_last_date', current_date)
raise
finally:
current_date = frappe.utils.add_days(current_date, 1)
frappe.db.commit()

View File

@ -1,20 +0,0 @@
data = {
'Manufacturing': {
'company_name': 'Wind Power LLC'
},
'Retail': {
'company_name': 'Mobile Next',
},
'Distribution': {
'company_name': 'Soltice Hardware',
},
'Services': {
'company_name': 'Acme Consulting'
},
'Education': {
'company_name': 'Whitmore College'
},
'Non Profit': {
'company_name': 'Erpnext Foundation'
}
}

View File

@ -1,181 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import json
import random
from datetime import datetime
import frappe
from frappe.utils.make_random import get_random
from erpnext.demo.setup.setup_data import import_json
def setup_data():
frappe.flags.mute_emails = True
make_masters()
setup_item()
make_student_applicants()
make_student_group()
make_fees_category()
make_fees_structure()
make_assessment_groups()
frappe.db.commit()
frappe.clear_cache()
def make_masters():
import_json("Room")
import_json("Department")
import_json("Instructor")
import_json("Course")
import_json("Program")
import_json("Student Batch Name")
import_json("Assessment Criteria")
import_json("Grading Scale")
frappe.db.commit()
def setup_item():
items = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'item_education.json')).read())
for i in items:
item = frappe.new_doc('Item')
item.update(i)
item.min_order_qty = random.randint(10, 30)
item.item_defaults[0].default_warehouse = frappe.get_all('Warehouse',
filters={'warehouse_name': item.item_defaults[0].default_warehouse}, limit=1)[0].name
item.insert()
def make_student_applicants():
blood_group = ["A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"]
male_names = []
female_names = []
file_path = get_json_path("Random Student Data")
with open(file_path, "r") as open_file:
random_student_data = json.loads(open_file.read())
count = 1
for d in random_student_data:
if d.get('gender') == "Male":
male_names.append(d.get('first_name').title())
if d.get('gender') == "Female":
female_names.append(d.get('first_name').title())
for idx, d in enumerate(random_student_data):
student_applicant = frappe.new_doc("Student Applicant")
student_applicant.first_name = d.get('first_name').title()
student_applicant.last_name = d.get('last_name').title()
student_applicant.image = d.get('image')
student_applicant.gender = d.get('gender')
student_applicant.program = get_random("Program")
student_applicant.blood_group = random.choice(blood_group)
year = random.randint(1990, 1998)
month = random.randint(1, 12)
day = random.randint(1, 28)
student_applicant.date_of_birth = datetime(year, month, day)
student_applicant.mother_name = random.choice(female_names) + " " + d.get('last_name').title()
student_applicant.father_name = random.choice(male_names) + " " + d.get('last_name').title()
if student_applicant.gender == "Male":
student_applicant.middle_name = random.choice(male_names)
else:
student_applicant.middle_name = random.choice(female_names)
student_applicant.student_email_id = d.get('first_name') + "_" + \
student_applicant.middle_name + "_" + d.get('last_name') + "@example.com"
if count <5:
student_applicant.insert()
frappe.db.commit()
else:
student_applicant.submit()
frappe.db.commit()
count+=1
def make_student_group():
for term in frappe.db.get_list("Academic Term"):
for program in frappe.db.get_list("Program"):
sg_tool = frappe.new_doc("Student Group Creation Tool")
sg_tool.academic_year = "2017-18"
sg_tool.academic_term = term.name
sg_tool.program = program.name
for d in sg_tool.get_courses():
d = frappe._dict(d)
student_group = frappe.new_doc("Student Group")
student_group.student_group_name = d.student_group_name
student_group.group_based_on = d.group_based_on
student_group.program = program.name
student_group.course = d.course
student_group.batch = d.batch
student_group.academic_term = term.name
student_group.academic_year = "2017-18"
student_group.save()
frappe.db.commit()
def make_fees_category():
fee_type = ["Tuition Fee", "Hostel Fee", "Logistics Fee",
"Medical Fee", "Mess Fee", "Security Deposit"]
fee_desc = {"Tuition Fee" : "Curricular activities which includes books, notebooks and faculty charges" ,
"Hostel Fee" : "Stay of students in institute premises",
"Logistics Fee" : "Lodging boarding of the students" ,
"Medical Fee" : "Medical welfare of the students",
"Mess Fee" : "Food and beverages for your ward",
"Security Deposit" : "In case your child is found to have damaged institutes property"
}
for i in fee_type:
fee_category = frappe.new_doc("Fee Category")
fee_category.category_name = i
fee_category.description = fee_desc[i]
fee_category.insert()
frappe.db.commit()
def make_fees_structure():
for d in frappe.db.get_list("Program"):
program = frappe.get_doc("Program", d.name)
for academic_term in ["2017-18 (Semester 1)", "2017-18 (Semester 2)", "2017-18 (Semester 3)"]:
fee_structure = frappe.new_doc("Fee Structure")
fee_structure.program = d.name
fee_structure.academic_term = random.choice(frappe.db.get_list("Academic Term")).name
for j in range(1,4):
temp = {"fees_category": random.choice(frappe.db.get_list("Fee Category")).name , "amount" : random.randint(500,1000)}
fee_structure.append("components", temp)
fee_structure.insert()
program.append("fees", {"academic_term": academic_term, "fee_structure": fee_structure.name, "amount": fee_structure.total_amount})
program.save()
frappe.db.commit()
def make_assessment_groups():
for year in frappe.db.get_list("Academic Year"):
ag = frappe.new_doc('Assessment Group')
ag.assessment_group_name = year.name
ag.parent_assessment_group = "All Assessment Groups"
ag.is_group = 1
ag.insert()
for term in frappe.db.get_list("Academic Term", filters = {"academic_year": year.name}):
ag1 = frappe.new_doc('Assessment Group')
ag1.assessment_group_name = term.name
ag1.parent_assessment_group = ag.name
ag1.is_group = 1
ag1.insert()
for assessment_group in ['Term I', 'Term II']:
ag2 = frappe.new_doc('Assessment Group')
ag2.assessment_group_name = ag1.name + " " + assessment_group
ag2.parent_assessment_group = ag1.name
ag2.insert()
frappe.db.commit()
def get_json_path(doctype):
return frappe.get_app_path('erpnext', 'demo', 'data', frappe.scrub(doctype) + '.json')
def weighted_choice(weights):
totals = []
running_total = 0
for w in weights:
running_total += w
totals.append(running_total)
rnd = random.random() * running_total
for i, total in enumerate(totals):
if rnd < total:
return i

View File

@ -1,140 +0,0 @@
import json
import random
import frappe
from frappe.utils import add_days, nowdate
from erpnext.demo.domains import data
from erpnext.demo.setup.setup_data import import_json
def setup_data():
import_json("Location")
import_json("Asset Category")
setup_item()
setup_workstation()
setup_asset()
import_json('Operation')
setup_item_price()
show_item_groups_in_website()
import_json('BOM', submit=True)
frappe.db.commit()
frappe.clear_cache()
def setup_workstation():
workstations = [u'Drilling Machine 1', u'Lathe 1', u'Assembly Station 1', u'Assembly Station 2', u'Packing and Testing Station']
for w in workstations:
frappe.get_doc({
"doctype": "Workstation",
"workstation_name": w,
"holiday_list": frappe.get_all("Holiday List")[0].name,
"hour_rate_consumable": int(random.random() * 20),
"hour_rate_electricity": int(random.random() * 10),
"hour_rate_labour": int(random.random() * 40),
"hour_rate_rent": int(random.random() * 10),
"working_hours": [
{
"enabled": 1,
"start_time": "8:00:00",
"end_time": "15:00:00"
}
]
}).insert()
def show_item_groups_in_website():
"""set show_in_website=1 for Item Groups"""
products = frappe.get_doc("Item Group", "Products")
products.show_in_website = 1
products.route = 'products'
products.save()
def setup_asset():
assets = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'asset.json')).read())
for d in assets:
asset = frappe.new_doc('Asset')
asset.update(d)
asset.purchase_date = add_days(nowdate(), -random.randint(20, 1500))
asset.next_depreciation_date = add_days(asset.purchase_date, 30)
asset.warehouse = "Stores - WPL"
asset.set_missing_values()
asset.make_depreciation_schedule()
asset.flags.ignore_validate = True
asset.flags.ignore_mandatory = True
asset.save()
asset.submit()
def setup_item():
items = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'item.json')).read())
for i in items:
item = frappe.new_doc('Item')
item.update(i)
if hasattr(item, 'item_defaults') and item.item_defaults[0].default_warehouse:
item.item_defaults[0].company = data.get("Manufacturing").get('company_name')
warehouse = frappe.get_all('Warehouse', filters={'warehouse_name': item.item_defaults[0].default_warehouse}, limit=1)
if warehouse:
item.item_defaults[0].default_warehouse = warehouse[0].name
item.insert()
def setup_product_bundle():
frappe.get_doc({
'doctype': 'Product Bundle',
'new_item_code': 'Wind Mill A Series with Spare Bearing',
'items': [
{'item_code': 'Wind Mill A Series', 'qty': 1},
{'item_code': 'Bearing Collar', 'qty': 1},
{'item_code': 'Bearing Assembly', 'qty': 1},
]
}).insert()
def setup_item_price():
frappe.db.sql("delete from `tabItem Price`")
standard_selling = {
"Base Bearing Plate": 28,
"Base Plate": 21,
"Bearing Assembly": 300,
"Bearing Block": 14,
"Bearing Collar": 103.6,
"Bearing Pipe": 63,
"Blade Rib": 46.2,
"Disc Collars": 42,
"External Disc": 56,
"Internal Disc": 70,
"Shaft": 340,
"Stand": 400,
"Upper Bearing Plate": 300,
"Wind Mill A Series": 320,
"Wind Mill A Series with Spare Bearing": 750,
"Wind MIll C Series": 400,
"Wind Turbine": 400,
"Wing Sheet": 30.8
}
standard_buying = {
"Base Bearing Plate": 20,
"Base Plate": 28,
"Base Plate Un Painted": 16,
"Bearing Block": 13,
"Bearing Collar": 96.4,
"Bearing Pipe": 55,
"Blade Rib": 38,
"Disc Collars": 34,
"External Disc": 50,
"Internal Disc": 60,
"Shaft": 250,
"Stand": 300,
"Upper Bearing Plate": 200,
"Wing Sheet": 25
}
for price_list in ("standard_buying", "standard_selling"):
for item, rate in locals().get(price_list).items():
frappe.get_doc({
"doctype": "Item Price",
"price_list": price_list.replace("_", " ").title(),
"item_code": item,
"selling": 1 if price_list=="standard_selling" else 0,
"buying": 1 if price_list=="standard_buying" else 0,
"price_list_rate": rate,
"currency": "USD"
}).insert()

View File

@ -1,62 +0,0 @@
import json
import frappe
from erpnext.demo.domains import data
def setup_data():
setup_item()
setup_item_price()
frappe.db.commit()
frappe.clear_cache()
def setup_item():
items = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'item.json')).read())
for i in items:
if not i.get("domain") == "Retail": continue
item = frappe.new_doc('Item')
item.update(i)
if hasattr(item, 'item_defaults') and item.item_defaults[0].default_warehouse:
item.item_defaults[0].company = data.get("Retail").get('company_name')
warehouse = frappe.get_all('Warehouse', filters={'warehouse_name': item.item_defaults[0].default_warehouse}, limit=1)
if warehouse:
item.item_defaults[0].default_warehouse = warehouse[0].name
item.insert()
def setup_item_price():
frappe.db.sql("delete from `tabItem Price`")
standard_selling = {
"OnePlus 6": 579,
"OnePlus 6T": 600,
"Xiaomi Poco F1": 300,
"Iphone XS": 999,
"Samsung Galaxy S9": 720,
"Sony Bluetooth Headphone": 99,
"Xiaomi Phone Repair": 10,
"Samsung Phone Repair": 20,
"OnePlus Phone Repair": 15,
"Apple Phone Repair": 30,
}
standard_buying = {
"OnePlus 6": 300,
"OnePlus 6T": 350,
"Xiaomi Poco F1": 200,
"Iphone XS": 600,
"Samsung Galaxy S9": 500,
"Sony Bluetooth Headphone": 69
}
for price_list in ("standard_buying", "standard_selling"):
for item, rate in locals().get(price_list).items():
frappe.get_doc({
"doctype": "Item Price",
"price_list": price_list.replace("_", " ").title(),
"item_code": item,
"selling": 1 if price_list=="standard_selling" else 0,
"buying": 1 if price_list=="standard_buying" else 0,
"price_list_rate": rate,
"currency": "USD"
}).insert()

View File

@ -1,447 +0,0 @@
import json
import random
import frappe
from frappe import _
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.utils import cstr, flt, now_datetime, random_string
from frappe.utils.make_random import add_random_children, get_random
from frappe.utils.nestedset import get_root_of
import erpnext
from erpnext.demo.domains import data
def setup(domain):
frappe.flags.in_demo = 1
complete_setup(domain)
setup_demo_page()
setup_fiscal_year()
setup_holiday_list()
setup_user()
setup_employee()
setup_user_roles(domain)
setup_role_permissions()
setup_custom_field_for_domain()
employees = frappe.get_all('Employee', fields=['name', 'date_of_joining'])
# monthly salary
setup_salary_structure(employees[:5], 0)
# based on timesheet
setup_salary_structure(employees[5:], 1)
setup_leave_allocation()
setup_customer()
setup_supplier()
setup_warehouse()
import_json('Address')
import_json('Contact')
import_json('Lead')
setup_currency_exchange()
#setup_mode_of_payment()
setup_account_to_expense_type()
setup_budget()
setup_pos_profile()
frappe.db.commit()
frappe.clear_cache()
def complete_setup(domain='Manufacturing'):
print("Complete Setup...")
from frappe.desk.page.setup_wizard.setup_wizard import setup_complete
if not frappe.get_all('Company', limit=1):
setup_complete({
"full_name": "Test User",
"email": "test_demo@erpnext.com",
"company_tagline": 'Awesome Products and Services',
"password": "demo",
"fy_start_date": "2015-01-01",
"fy_end_date": "2015-12-31",
"bank_account": "National Bank",
"domains": [domain],
"company_name": data.get(domain).get('company_name'),
"chart_of_accounts": "Standard",
"company_abbr": ''.join([d[0] for d in data.get(domain).get('company_name').split()]).upper(),
"currency": 'USD',
"timezone": 'America/New_York',
"country": 'United States',
"language": "english"
})
company = erpnext.get_default_company()
if company:
company_doc = frappe.get_doc("Company", company)
company_doc.db_set('default_payroll_payable_account',
frappe.db.get_value('Account', dict(account_name='Payroll Payable')))
def setup_demo_page():
# home page should always be "start"
website_settings = frappe.get_doc("Website Settings", "Website Settings")
website_settings.home_page = "demo"
website_settings.save()
def setup_fiscal_year():
fiscal_year = None
for year in range(2010, now_datetime().year + 1, 1):
try:
fiscal_year = frappe.get_doc({
"doctype": "Fiscal Year",
"year": cstr(year),
"year_start_date": "{0}-01-01".format(year),
"year_end_date": "{0}-12-31".format(year)
}).insert()
except frappe.DuplicateEntryError:
pass
# set the last fiscal year (current year) as default
if fiscal_year:
fiscal_year.set_as_default()
def setup_holiday_list():
"""Setup Holiday List for the current year"""
year = now_datetime().year
holiday_list = frappe.get_doc({
"doctype": "Holiday List",
"holiday_list_name": str(year),
"from_date": "{0}-01-01".format(year),
"to_date": "{0}-12-31".format(year),
})
holiday_list.insert()
holiday_list.weekly_off = "Saturday"
holiday_list.get_weekly_off_dates()
holiday_list.weekly_off = "Sunday"
holiday_list.get_weekly_off_dates()
holiday_list.save()
frappe.set_value("Company", erpnext.get_default_company(), "default_holiday_list", holiday_list.name)
def setup_user():
frappe.db.sql('delete from tabUser where name not in ("Guest", "Administrator")')
for u in json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'user.json')).read()):
user = frappe.new_doc("User")
user.update(u)
user.flags.no_welcome_mail = True
user.new_password = 'Demo1234567!!!'
user.insert()
def setup_employee():
frappe.db.set_value("HR Settings", None, "emp_created_by", "Naming Series")
frappe.db.commit()
for d in frappe.get_all('Salary Component'):
salary_component = frappe.get_doc('Salary Component', d.name)
salary_component.append('accounts', dict(
company=erpnext.get_default_company(),
account=frappe.get_value('Account', dict(account_name=('like', 'Salary%')))
))
salary_component.save()
import_json('Employee')
holiday_list = frappe.db.get_value("Holiday List", {"holiday_list_name": str(now_datetime().year)}, 'name')
frappe.db.sql('''update tabEmployee set holiday_list={0}'''.format(holiday_list))
def setup_salary_structure(employees, salary_slip_based_on_timesheet=0):
ss = frappe.new_doc('Salary Structure')
ss.name = "Sample Salary Structure - " + random_string(5)
ss.salary_slip_based_on_timesheet = salary_slip_based_on_timesheet
if salary_slip_based_on_timesheet:
ss.salary_component = 'Basic'
ss.hour_rate = flt(random.random() * 10, 2)
else:
ss.payroll_frequency = 'Monthly'
ss.payment_account = frappe.get_value('Account',
{'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
ss.append('earnings', {
'salary_component': 'Basic',
"abbr":'B',
'formula': 'base*.2',
'amount_based_on_formula': 1,
"idx": 1
})
ss.append('deductions', {
'salary_component': 'Income Tax',
"abbr":'IT',
'condition': 'base > 10000',
'formula': 'base*.1',
"idx": 1
})
ss.insert()
ss.submit()
for e in employees:
sa = frappe.new_doc("Salary Structure Assignment")
sa.employee = e.name
sa.salary_structure = ss.name
sa.from_date = "2015-01-01"
sa.base = random.random() * 10000
sa.insert()
sa.submit()
return ss
def setup_user_roles(domain):
user = frappe.get_doc('User', 'demo@erpnext.com')
user.add_roles('HR User', 'HR Manager', 'Accounts User', 'Accounts Manager',
'Stock User', 'Stock Manager', 'Sales User', 'Sales Manager', 'Purchase User',
'Purchase Manager', 'Projects User', 'Manufacturing User', 'Manufacturing Manager',
'Support Team')
if domain == "Education":
user.add_roles('Academics User')
if not frappe.db.get_global('demo_hr_user'):
user = frappe.get_doc('User', 'CaitlinSnow@example.com')
user.add_roles('HR User', 'HR Manager', 'Accounts User')
frappe.db.set_global('demo_hr_user', user.name)
update_employee_department(user.name, 'Human Resources')
for d in frappe.get_all('User Permission', filters={"user": "CaitlinSnow@example.com"}):
frappe.delete_doc('User Permission', d.name)
if not frappe.db.get_global('demo_sales_user_1'):
user = frappe.get_doc('User', 'VandalSavage@example.com')
user.add_roles('Sales User')
update_employee_department(user.name, 'Sales')
frappe.db.set_global('demo_sales_user_1', user.name)
if not frappe.db.get_global('demo_sales_user_2'):
user = frappe.get_doc('User', 'GraceChoi@example.com')
user.add_roles('Sales User', 'Sales Manager', 'Accounts User')
update_employee_department(user.name, 'Sales')
frappe.db.set_global('demo_sales_user_2', user.name)
if not frappe.db.get_global('demo_purchase_user'):
user = frappe.get_doc('User', 'MaxwellLord@example.com')
user.add_roles('Purchase User', 'Purchase Manager', 'Accounts User', 'Stock User')
update_employee_department(user.name, 'Purchase')
frappe.db.set_global('demo_purchase_user', user.name)
if not frappe.db.get_global('demo_manufacturing_user'):
user = frappe.get_doc('User', 'NeptuniaAquaria@example.com')
user.add_roles('Manufacturing User', 'Stock Manager', 'Stock User', 'Purchase User', 'Accounts User')
update_employee_department(user.name, 'Production')
frappe.db.set_global('demo_manufacturing_user', user.name)
if not frappe.db.get_global('demo_stock_user'):
user = frappe.get_doc('User', 'HollyGranger@example.com')
user.add_roles('Manufacturing User', 'Stock User', 'Purchase User', 'Accounts User')
update_employee_department(user.name, 'Production')
frappe.db.set_global('demo_stock_user', user.name)
if not frappe.db.get_global('demo_accounts_user'):
user = frappe.get_doc('User', 'BarryAllen@example.com')
user.add_roles('Accounts User', 'Accounts Manager', 'Sales User', 'Purchase User')
update_employee_department(user.name, 'Accounts')
frappe.db.set_global('demo_accounts_user', user.name)
if not frappe.db.get_global('demo_projects_user'):
user = frappe.get_doc('User', 'PeterParker@example.com')
user.add_roles('HR User', 'Projects User')
update_employee_department(user.name, 'Management')
frappe.db.set_global('demo_projects_user', user.name)
if domain == "Education":
if not frappe.db.get_global('demo_education_user'):
user = frappe.get_doc('User', 'ArthurCurry@example.com')
user.add_roles('Academics User')
update_employee_department(user.name, 'Management')
frappe.db.set_global('demo_education_user', user.name)
#Add Expense Approver
user = frappe.get_doc('User', 'ClarkKent@example.com')
user.add_roles('Expense Approver')
def setup_leave_allocation():
year = now_datetime().year
for employee in frappe.get_all('Employee', fields=['name']):
leave_types = frappe.get_all("Leave Type", fields=['name', 'max_continuous_days_allowed'])
for leave_type in leave_types:
if not leave_type.max_continuous_days_allowed:
leave_type.max_continuous_days_allowed = 10
leave_allocation = frappe.get_doc({
"doctype": "Leave Allocation",
"employee": employee.name,
"from_date": "{0}-01-01".format(year),
"to_date": "{0}-12-31".format(year),
"leave_type": leave_type.name,
"new_leaves_allocated": random.randint(1, int(leave_type.max_continuous_days_allowed))
})
leave_allocation.insert()
leave_allocation.submit()
frappe.db.commit()
def setup_customer():
customers = [u'Asian Junction', u'Life Plan Counselling', u'Two Pesos', u'Mr Fables', u'Intelacard', u'Big D Supermarkets', u'Adaptas', u'Nelson Brothers', u'Landskip Yard Care', u'Buttrey Food & Drug', u'Fayva', u'Asian Fusion', u'Crafts Canada', u'Consumers and Consumers Express', u'Netobill', u'Choices', u'Chi-Chis', u'Red Food', u'Endicott Shoes', u'Hind Enterprises']
for c in customers:
frappe.get_doc({
"doctype": "Customer",
"customer_name": c,
"customer_group": "Commercial",
"customer_type": random.choice(["Company", "Individual"]),
"territory": "Rest Of The World"
}).insert()
def setup_supplier():
suppliers = [u'Helios Air', u'Ks Merchandise', u'HomeBase', u'Scott Ties', u'Reliable Investments', u'Nan Duskin', u'Rainbow Records', u'New World Realty', u'Asiatic Solutions', u'Eagle Hardware', u'Modern Electricals']
for s in suppliers:
frappe.get_doc({
"doctype": "Supplier",
"supplier_name": s,
"supplier_group": random.choice(["Services", "Raw Material"]),
}).insert()
def setup_warehouse():
w = frappe.new_doc('Warehouse')
w.warehouse_name = 'Supplier'
w.insert()
def setup_currency_exchange():
frappe.get_doc({
'doctype': 'Currency Exchange',
'from_currency': 'EUR',
'to_currency': 'USD',
'exchange_rate': 1.13
}).insert()
frappe.get_doc({
'doctype': 'Currency Exchange',
'from_currency': 'CNY',
'to_currency': 'USD',
'exchange_rate': 0.16
}).insert()
def setup_mode_of_payment():
company_abbr = frappe.get_cached_value('Company', erpnext.get_default_company(), "abbr")
account_dict = {'Cash': 'Cash - '+ company_abbr , 'Bank': 'National Bank - '+ company_abbr}
for payment_mode in frappe.get_all('Mode of Payment', fields = ["name", "type"]):
if payment_mode.type:
mop = frappe.get_doc('Mode of Payment', payment_mode.name)
mop.append('accounts', {
'company': erpnext.get_default_company(),
'default_account': account_dict.get(payment_mode.type)
})
mop.save(ignore_permissions=True)
def setup_account():
frappe.flags.in_import = True
data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data',
'account.json')).read())
for d in data:
doc = frappe.new_doc('Account')
doc.update(d)
doc.parent_account = frappe.db.get_value('Account', {'account_name': doc.parent_account})
doc.insert()
frappe.flags.in_import = False
def setup_account_to_expense_type():
company_abbr = frappe.get_cached_value('Company', erpnext.get_default_company(), "abbr")
expense_types = [{'name': _('Calls'), "account": "Sales Expenses - "+ company_abbr},
{'name': _('Food'), "account": "Entertainment Expenses - "+ company_abbr},
{'name': _('Medical'), "account": "Utility Expenses - "+ company_abbr},
{'name': _('Others'), "account": "Miscellaneous Expenses - "+ company_abbr},
{'name': _('Travel'), "account": "Travel Expenses - "+ company_abbr}]
for expense_type in expense_types:
doc = frappe.get_doc("Expense Claim Type", expense_type["name"])
doc.append("accounts", {
"company" : erpnext.get_default_company(),
"default_account" : expense_type["account"]
})
doc.save(ignore_permissions=True)
def setup_budget():
fiscal_years = frappe.get_all("Fiscal Year", order_by="year_start_date")[-2:]
for fy in fiscal_years:
budget = frappe.new_doc("Budget")
budget.cost_center = get_random("Cost Center")
budget.fiscal_year = fy.name
budget.action_if_annual_budget_exceeded = "Warn"
expense_ledger_count = frappe.db.count("Account", {"is_group": "0", "root_type": "Expense"})
add_random_children(budget, "accounts", rows=random.randint(10, expense_ledger_count),
randomize = {
"account": ("Account", {"is_group": "0", "root_type": "Expense"})
}, unique="account")
for d in budget.accounts:
d.budget_amount = random.randint(5, 100) * 10000
budget.save()
budget.submit()
def setup_pos_profile():
company_abbr = frappe.get_cached_value('Company', erpnext.get_default_company(), "abbr")
pos = frappe.new_doc('POS Profile')
pos.user = frappe.db.get_global('demo_accounts_user')
pos.name = "Demo POS Profile"
pos.naming_series = 'SINV-'
pos.update_stock = 0
pos.write_off_account = 'Cost of Goods Sold - '+ company_abbr
pos.write_off_cost_center = 'Main - '+ company_abbr
pos.customer_group = get_root_of('Customer Group')
pos.territory = get_root_of('Territory')
pos.append('payments', {
'mode_of_payment': frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name'),
'amount': 0.0,
'default': 1
})
pos.insert()
def setup_role_permissions():
role_permissions = {'Batch': ['Accounts User', 'Item Manager']}
for doctype, roles in role_permissions.items():
for role in roles:
if not frappe.db.get_value('Custom DocPerm',
{'parent': doctype, 'role': role}):
frappe.get_doc({
'doctype': 'Custom DocPerm',
'role': role,
'read': 1,
'write': 1,
'create': 1,
'delete': 1,
'parent': doctype
}).insert(ignore_permissions=True)
def import_json(doctype, submit=False, values=None):
frappe.flags.in_import = True
data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data',
frappe.scrub(doctype) + '.json')).read())
for d in data:
doc = frappe.new_doc(doctype)
doc.update(d)
doc.insert()
if submit:
doc.submit()
frappe.db.commit()
frappe.flags.in_import = False
def update_employee_department(user_id, department):
employee = frappe.db.get_value('Employee', {"user_id": user_id}, 'name')
department = frappe.db.get_value('Department', {'department_name': department}, 'name')
frappe.db.set_value('Employee', employee, 'department', department)
def setup_custom_field_for_domain():
field = {
"Item": [
dict(fieldname='domain', label='Domain',
fieldtype='Select', hidden=1, default="Manufacturing",
options="Manufacturing\nService\nDistribution\nRetail"
)
]
}
create_custom_fields(field)

View File

@ -1,127 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import random
import frappe
from frappe.desk import query_report
from frappe.utils import random_string
from frappe.utils.make_random import get_random
import erpnext
from erpnext.accounts.doctype.journal_entry.journal_entry import get_payment_entry_against_invoice
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.payment_request.payment_request import (
make_payment_entry,
make_payment_request,
)
from erpnext.demo.user.sales import make_sales_order
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
def work():
frappe.set_user(frappe.db.get_global('demo_accounts_user'))
if random.random() <= 0.6:
report = "Ordered Items to be Billed"
for so in list(set([r[0] for r in query_report.run(report)["result"]
if r[0]!="Total"]))[:random.randint(1, 5)]:
try:
si = frappe.get_doc(make_sales_invoice(so))
si.posting_date = frappe.flags.current_date
for d in si.get("items"):
if not d.income_account:
d.income_account = "Sales - {}".format(frappe.get_cached_value('Company', si.company, 'abbr'))
si.insert()
si.submit()
frappe.db.commit()
except frappe.ValidationError:
pass
if random.random() <= 0.6:
report = "Received Items to be Billed"
for pr in list(set([r[0] for r in query_report.run(report)["result"]
if r[0]!="Total"]))[:random.randint(1, 5)]:
try:
pi = frappe.get_doc(make_purchase_invoice(pr))
pi.posting_date = frappe.flags.current_date
pi.bill_no = random_string(6)
pi.insert()
pi.submit()
frappe.db.commit()
except frappe.ValidationError:
pass
if random.random() < 0.5:
make_payment_entries("Sales Invoice", "Accounts Receivable")
if random.random() < 0.5:
make_payment_entries("Purchase Invoice", "Accounts Payable")
if random.random() < 0.4:
#make payment request against sales invoice
sales_invoice_name = get_random("Sales Invoice", filters={"docstatus": 1})
if sales_invoice_name:
si = frappe.get_doc("Sales Invoice", sales_invoice_name)
if si.outstanding_amount > 0:
payment_request = make_payment_request(dt="Sales Invoice", dn=si.name, recipient_id=si.contact_email,
submit_doc=True, mute_email=True, use_dummy_message=True)
payment_entry = frappe.get_doc(make_payment_entry(payment_request.name))
payment_entry.posting_date = frappe.flags.current_date
payment_entry.submit()
make_pos_invoice()
def make_payment_entries(ref_doctype, report):
outstanding_invoices = frappe.get_all(ref_doctype, fields=["name"],
filters={
"company": erpnext.get_default_company(),
"outstanding_amount": (">", 0.0)
})
# make Payment Entry
for inv in outstanding_invoices[:random.randint(1, 2)]:
pe = get_payment_entry(ref_doctype, inv.name)
pe.posting_date = frappe.flags.current_date
pe.reference_no = random_string(6)
pe.reference_date = frappe.flags.current_date
pe.insert()
pe.submit()
frappe.db.commit()
outstanding_invoices.remove(inv)
# make payment via JV
for inv in outstanding_invoices[:1]:
jv = frappe.get_doc(get_payment_entry_against_invoice(ref_doctype, inv.name))
jv.posting_date = frappe.flags.current_date
jv.cheque_no = random_string(6)
jv.cheque_date = frappe.flags.current_date
jv.insert()
jv.submit()
frappe.db.commit()
def make_pos_invoice():
make_sales_order()
for data in frappe.get_all('Sales Order', fields=["name"],
filters = [["per_billed", "<", "100"]]):
si = frappe.get_doc(make_sales_invoice(data.name))
si.is_pos =1
si.posting_date = frappe.flags.current_date
for d in si.get("items"):
if not d.income_account:
d.income_account = "Sales - {}".format(frappe.get_cached_value('Company', si.company, 'abbr'))
si.set_missing_values()
make_payment_entries_for_pos_invoice(si)
si.insert()
si.submit()
def make_payment_entries_for_pos_invoice(si):
for data in si.payments:
data.amount = si.outstanding_amount
return

View File

@ -1,123 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import random
from datetime import timedelta
import frappe
from frappe.utils import cstr
from frappe.utils.make_random import get_random
from erpnext.education.api import (
collect_fees,
enroll_student,
get_course,
get_fee_schedule,
get_student_group_students,
make_attendance_records,
)
def work():
frappe.set_user(frappe.db.get_global('demo_education_user'))
for d in range(20):
approve_random_student_applicant()
enroll_random_student(frappe.flags.current_date)
# if frappe.flags.current_date.weekday()== 0:
# make_course_schedule(frappe.flags.current_date, frappe.utils.add_days(frappe.flags.current_date, 5))
mark_student_attendance(frappe.flags.current_date)
# make_assessment_plan()
make_fees()
def approve_random_student_applicant():
random_student = get_random("Student Applicant", {"application_status": "Applied"})
if random_student:
status = ["Approved", "Rejected"]
frappe.db.set_value("Student Applicant", random_student, "application_status", status[weighted_choice([9,3])])
def enroll_random_student(current_date):
batch = ["Section-A", "Section-B"]
random_student = get_random("Student Applicant", {"application_status": "Approved"})
if random_student:
enrollment = enroll_student(random_student)
enrollment.academic_year = get_random("Academic Year")
enrollment.enrollment_date = current_date
enrollment.student_batch_name = batch[weighted_choice([9,3])]
fee_schedule = get_fee_schedule(enrollment.program)
for fee in fee_schedule:
enrollment.append("fees", fee)
enrolled_courses = get_course(enrollment.program)
for course in enrolled_courses:
enrollment.append("courses", course)
enrollment.submit()
frappe.db.commit()
assign_student_group(enrollment.student, enrollment.student_name, enrollment.program,
enrolled_courses, enrollment.student_batch_name)
def assign_student_group(student, student_name, program, courses, batch):
course_list = [d["course"] for d in courses]
for d in frappe.get_list("Student Group", fields=("name"), filters={"program": program, "course":("in", course_list), "disabled": 0}):
student_group = frappe.get_doc("Student Group", d.name)
student_group.append("students", {"student": student, "student_name": student_name,
"group_roll_number":len(student_group.students)+1, "active":1})
student_group.save()
student_batch = frappe.get_list("Student Group", fields=("name"), filters={"program": program, "group_based_on":"Batch", "batch":batch, "disabled": 0})[0]
student_batch_doc = frappe.get_doc("Student Group", student_batch.name)
student_batch_doc.append("students", {"student": student, "student_name": student_name,
"group_roll_number":len(student_batch_doc.students)+1, "active":1})
student_batch_doc.save()
frappe.db.commit()
def mark_student_attendance(current_date):
status = ["Present", "Absent"]
for d in frappe.db.get_list("Student Group", filters={"group_based_on": "Batch", "disabled": 0}):
students = get_student_group_students(d.name)
for stud in students:
make_attendance_records(stud.student, stud.student_name, status[weighted_choice([9,4])], None, d.name, current_date)
def make_fees():
for d in range(1,10):
random_fee = get_random("Fees", {"paid_amount": 0})
collect_fees(random_fee, frappe.db.get_value("Fees", random_fee, "outstanding_amount"))
def make_assessment_plan(date):
for d in range(1,4):
random_group = get_random("Student Group", {"group_based_on": "Course", "disabled": 0}, True)
doc = frappe.new_doc("Assessment Plan")
doc.student_group = random_group.name
doc.course = random_group.course
doc.assessment_group = get_random("Assessment Group", {"is_group": 0, "parent": "2017-18 (Semester 2)"})
doc.grading_scale = get_random("Grading Scale")
doc.maximum_assessment_score = 100
def make_course_schedule(start_date, end_date):
for d in frappe.db.get_list("Student Group"):
cs = frappe.new_doc("Scheduling Tool")
cs.student_group = d.name
cs.room = get_random("Room")
cs.instructor = get_random("Instructor")
cs.course_start_date = cstr(start_date)
cs.course_end_date = cstr(end_date)
day = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
for x in range(3):
random_day = random.choice(day)
cs.day = random_day
cs.from_time = timedelta(hours=(random.randrange(7, 17,1)))
cs.to_time = cs.from_time + timedelta(hours=1)
cs.schedule_course()
day.remove(random_day)
def weighted_choice(weights):
totals = []
running_total = 0
for w in weights:
running_total += w
totals.append(running_total)
rnd = random.random() * running_total
for i, total in enumerate(totals):
if rnd < total:
return i

View File

@ -1,44 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
from frappe.utils.make_random import get_random
from erpnext.assets.doctype.asset.asset import make_sales_invoice
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset
def work():
frappe.set_user(frappe.db.get_global('demo_accounts_user'))
# Enable booking asset depreciation entry automatically
frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
# post depreciation entries as on today
post_depreciation_entries()
# scrap a random asset
frappe.db.set_value("Company", "Wind Power LLC", "disposal_account", "Gain/Loss on Asset Disposal - WPL")
asset = get_random_asset()
scrap_asset(asset.name)
# Sell a random asset
sell_an_asset()
def sell_an_asset():
asset = get_random_asset()
si = make_sales_invoice(asset.name, asset.item_code, "Wind Power LLC")
si.customer = get_random("Customer")
si.get("items")[0].rate = asset.value_after_depreciation * 0.8 \
if asset.value_after_depreciation else asset.gross_purchase_amount * 0.9
si.save()
si.submit()
def get_random_asset():
return frappe.db.sql(""" select name, item_code, value_after_depreciation, gross_purchase_amount
from `tabAsset`
where docstatus=1 and status not in ("Scrapped", "Sold") order by rand() limit 1""", as_dict=1)[0]

View File

@ -1,223 +0,0 @@
import datetime
import random
import frappe
from frappe.utils import add_days, get_last_day, getdate, random_string
from frappe.utils.make_random import get_random
import erpnext
from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
from erpnext.hr.doctype.expense_claim.test_expense_claim import get_payable_account
from erpnext.hr.doctype.leave_application.leave_application import (
AttendanceAlreadyMarkedError,
OverlapError,
get_leave_balance_on,
)
from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet
from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
def work():
frappe.set_user(frappe.db.get_global('demo_hr_user'))
year, month = frappe.flags.current_date.strftime("%Y-%m").split("-")
setup_department_approvers()
mark_attendance()
make_leave_application()
# payroll entry
if not frappe.db.sql('select name from `tabSalary Slip` where month(adddate(start_date, interval 1 month))=month(curdate())'):
# based on frequency
payroll_entry = get_payroll_entry()
payroll_entry.salary_slip_based_on_timesheet = 0
payroll_entry.save()
payroll_entry.create_salary_slips()
payroll_entry.submit_salary_slips()
payroll_entry.make_accrual_jv_entry()
payroll_entry.submit()
# payroll_entry.make_journal_entry(reference_date=frappe.flags.current_date,
# reference_number=random_string(10))
# based on timesheet
payroll_entry = get_payroll_entry()
payroll_entry.salary_slip_based_on_timesheet = 1
payroll_entry.save()
payroll_entry.create_salary_slips()
payroll_entry.submit_salary_slips()
payroll_entry.make_accrual_jv_entry()
payroll_entry.submit()
# payroll_entry.make_journal_entry(reference_date=frappe.flags.current_date,
# reference_number=random_string(10))
if frappe.db.get_global('demo_hr_user'):
make_timesheet_records()
#expense claim
expense_claim = frappe.new_doc("Expense Claim")
expense_claim.extend('expenses', get_expenses())
expense_claim.employee = get_random("Employee")
expense_claim.company = frappe.flags.company
expense_claim.payable_account = get_payable_account(expense_claim.company)
expense_claim.posting_date = frappe.flags.current_date
expense_claim.expense_approver = frappe.db.get_global('demo_hr_user')
expense_claim.save()
rand = random.random()
if rand < 0.4:
update_sanctioned_amount(expense_claim)
expense_claim.approval_status = 'Approved'
expense_claim.submit()
if random.randint(0, 1):
#make journal entry against expense claim
je = frappe.get_doc(make_bank_entry("Expense Claim", expense_claim.name))
je.posting_date = frappe.flags.current_date
je.cheque_no = random_string(10)
je.cheque_date = frappe.flags.current_date
je.flags.ignore_permissions = 1
je.submit()
def get_payroll_entry():
# process payroll for previous month
payroll_entry = frappe.new_doc("Payroll Entry")
payroll_entry.company = frappe.flags.company
payroll_entry.payroll_frequency = 'Monthly'
# select a posting date from the previous month
payroll_entry.posting_date = get_last_day(getdate(frappe.flags.current_date) - datetime.timedelta(days=10))
payroll_entry.payment_account = frappe.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
payroll_entry.set_start_end_dates()
return payroll_entry
def get_expenses():
expenses = []
expese_types = frappe.db.sql("""select ect.name, eca.default_account from `tabExpense Claim Type` ect,
`tabExpense Claim Account` eca where eca.parent=ect.name
and eca.company=%s """, frappe.flags.company,as_dict=1)
for expense_type in expese_types[:random.randint(1,4)]:
claim_amount = random.randint(1,20)*10
expenses.append({
"expense_date": frappe.flags.current_date,
"expense_type": expense_type.name,
"default_account": expense_type.default_account or "Miscellaneous Expenses - WPL",
"amount": claim_amount,
"sanctioned_amount": claim_amount
})
return expenses
def update_sanctioned_amount(expense_claim):
for expense in expense_claim.expenses:
sanctioned_amount = random.randint(1,20)*10
if sanctioned_amount < expense.amount:
expense.sanctioned_amount = sanctioned_amount
def get_timesheet_based_salary_slip_employee():
sal_struct = frappe.db.sql("""
select name from `tabSalary Structure`
where salary_slip_based_on_timesheet = 1
and docstatus != 2""")
if sal_struct:
employees = frappe.db.sql("""
select employee from `tabSalary Structure Assignment`
where salary_structure IN %(sal_struct)s""", {"sal_struct": sal_struct}, as_dict=True)
return employees
else:
return []
def make_timesheet_records():
employees = get_timesheet_based_salary_slip_employee()
for e in employees:
ts = make_timesheet(e.employee, simulate = True, billable = 1, activity_type=get_random("Activity Type"), company=frappe.flags.company)
frappe.db.commit()
rand = random.random()
if rand >= 0.3:
make_salary_slip_for_timesheet(ts.name)
rand = random.random()
if rand >= 0.2:
make_sales_invoice_for_timesheet(ts.name)
def make_salary_slip_for_timesheet(name):
salary_slip = make_salary_slip(name)
salary_slip.insert()
salary_slip.submit()
frappe.db.commit()
def make_sales_invoice_for_timesheet(name):
sales_invoice = make_sales_invoice(name)
sales_invoice.customer = get_random("Customer")
sales_invoice.append('items', {
'item_code': get_random("Item", {"has_variants": 0, "is_stock_item": 0,
"is_fixed_asset": 0}),
'qty': 1,
'rate': 1000
})
sales_invoice.flags.ignore_permissions = 1
sales_invoice.set_missing_values()
sales_invoice.calculate_taxes_and_totals()
sales_invoice.insert()
sales_invoice.submit()
frappe.db.commit()
def make_leave_application():
allocated_leaves = frappe.get_all("Leave Allocation", fields=['employee', 'leave_type'])
for allocated_leave in allocated_leaves:
leave_balance = get_leave_balance_on(allocated_leave.employee, allocated_leave.leave_type, frappe.flags.current_date,
consider_all_leaves_in_the_allocation_period=True)
if leave_balance != 0:
if leave_balance == 1:
to_date = frappe.flags.current_date
else:
to_date = add_days(frappe.flags.current_date, random.randint(0, leave_balance-1))
leave_application = frappe.get_doc({
"doctype": "Leave Application",
"employee": allocated_leave.employee,
"from_date": frappe.flags.current_date,
"to_date": to_date,
"leave_type": allocated_leave.leave_type,
})
try:
leave_application.insert()
leave_application.submit()
frappe.db.commit()
except (OverlapError, AttendanceAlreadyMarkedError):
frappe.db.rollback()
def mark_attendance():
attendance_date = frappe.flags.current_date
for employee in frappe.get_all('Employee', fields=['name'], filters = {'status': 'Active'}):
if not frappe.db.get_value("Attendance", {"employee": employee.name, "attendance_date": attendance_date}):
attendance = frappe.get_doc({
"doctype": "Attendance",
"employee": employee.name,
"attendance_date": attendance_date
})
leave = frappe.db.sql("""select name from `tabLeave Application`
where employee = %s and %s between from_date and to_date
and docstatus = 1""", (employee.name, attendance_date))
if leave:
attendance.status = "Absent"
else:
attendance.status = "Present"
attendance.save()
attendance.submit()
frappe.db.commit()
def setup_department_approvers():
for d in frappe.get_all('Department', filters={'department_name': ['!=', 'All Departments']}):
doc = frappe.get_doc('Department', d.name)
doc.append("leave_approvers", {'approver': frappe.session.user})
doc.append("expense_approvers", {'approver': frappe.session.user})
doc.flags.ignore_mandatory = True
doc.save()

View File

@ -1,123 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import random
from datetime import timedelta
import frappe
from frappe.desk import query_report
from frappe.utils.make_random import how_many
import erpnext
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
def work():
if random.random() < 0.3: return
frappe.set_user(frappe.db.get_global('demo_manufacturing_user'))
if not frappe.get_all('Sales Order'): return
ppt = frappe.new_doc("Production Plan")
ppt.company = erpnext.get_default_company()
# ppt.use_multi_level_bom = 1 #refactored
ppt.get_items_from = "Sales Order"
# ppt.purchase_request_for_warehouse = "Stores - WPL" # refactored
ppt.run_method("get_open_sales_orders")
if not ppt.get("sales_orders"): return
ppt.run_method("get_items")
ppt.run_method("raise_material_requests")
ppt.save()
ppt.submit()
ppt.run_method("raise_work_orders")
frappe.db.commit()
# submit work orders
for pro in frappe.db.get_values("Work Order", {"docstatus": 0}, "name"):
b = frappe.get_doc("Work Order", pro[0])
b.wip_warehouse = "Work in Progress - WPL"
b.submit()
frappe.db.commit()
# submit material requests
for pro in frappe.db.get_values("Material Request", {"docstatus": 0}, "name"):
b = frappe.get_doc("Material Request", pro[0])
b.submit()
frappe.db.commit()
# stores -> wip
if random.random() < 0.4:
for pro in query_report.run("Open Work Orders")["result"][:how_many("Stock Entry for WIP")]:
make_stock_entry_from_pro(pro[0], "Material Transfer for Manufacture")
# wip -> fg
if random.random() < 0.4:
for pro in query_report.run("Work Orders in Progress")["result"][:how_many("Stock Entry for FG")]:
make_stock_entry_from_pro(pro[0], "Manufacture")
for bom in frappe.get_all('BOM', fields=['item'], filters = {'with_operations': 1}):
pro_order = make_wo_order_test_record(item=bom.item, qty=2,
source_warehouse="Stores - WPL", wip_warehouse = "Work in Progress - WPL",
fg_warehouse = "Stores - WPL", company = erpnext.get_default_company(),
stock_uom = frappe.db.get_value('Item', bom.item, 'stock_uom'),
planned_start_date = frappe.flags.current_date)
# submit job card
if random.random() < 0.4:
submit_job_cards()
def make_stock_entry_from_pro(pro_id, purpose):
from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry
from erpnext.stock.doctype.stock_entry.stock_entry import (
DuplicateEntryForWorkOrderError,
IncorrectValuationRateError,
OperationsNotCompleteError,
)
from erpnext.stock.stock_ledger import NegativeStockError
try:
st = frappe.get_doc(make_stock_entry(pro_id, purpose))
st.posting_date = frappe.flags.current_date
st.fiscal_year = str(frappe.flags.current_date.year)
for d in st.get("items"):
d.cost_center = "Main - " + frappe.get_cached_value('Company', st.company, 'abbr')
st.insert()
frappe.db.commit()
st.submit()
frappe.db.commit()
except (NegativeStockError, IncorrectValuationRateError, DuplicateEntryForWorkOrderError,
OperationsNotCompleteError):
frappe.db.rollback()
def submit_job_cards():
work_orders = frappe.get_all("Work Order", ["name", "creation"], {"docstatus": 1, "status": "Not Started"})
work_order = random.choice(work_orders)
# for work_order in work_orders:
start_date = work_order.creation
work_order = frappe.get_doc("Work Order", work_order.name)
job = frappe.get_all("Job Card", ["name", "operation", "work_order"],
{"docstatus": 0, "work_order": work_order.name})
if not job: return
job_map = {}
for d in job:
job_map[d.operation] = frappe.get_doc("Job Card", d.name)
for operation in work_order.operations:
job = job_map[operation.operation]
job_time_log = frappe.new_doc("Job Card Time Log")
job_time_log.from_time = start_date
minutes = operation.get("time_in_mins")
job_time_log.time_in_mins = random.randint(int(minutes/2), minutes)
job_time_log.to_time = job_time_log.from_time + \
timedelta(minutes=job_time_log.time_in_mins)
job_time_log.parent = job.name
job_time_log.parenttype = 'Job Card'
job_time_log.parentfield = 'time_logs'
job_time_log.completed_qty = work_order.qty
job_time_log.save(ignore_permissions=True)
job.time_logs.append(job_time_log)
job.save(ignore_permissions=True)
job.submit()
start_date = job_time_log.to_time

View File

@ -1,44 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
from frappe.utils import flt
from frappe.utils.make_random import get_random
import erpnext
from erpnext.demo.user.hr import make_sales_invoice_for_timesheet
from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet
def run_projects(current_date):
frappe.set_user(frappe.db.get_global('demo_projects_user'))
if frappe.db.get_global('demo_projects_user'):
make_project(current_date)
make_timesheet_for_projects(current_date)
close_tasks(current_date)
def make_timesheet_for_projects(current_date ):
for data in frappe.get_all("Task", ["name", "project"], {"status": "Open", "exp_end_date": ("<", current_date)}):
employee = get_random("Employee")
ts = make_timesheet(employee, simulate = True, billable = 1, company = erpnext.get_default_company(),
activity_type=get_random("Activity Type"), project=data.project, task =data.name)
if flt(ts.total_billable_amount) > 0.0:
make_sales_invoice_for_timesheet(ts.name)
frappe.db.commit()
def close_tasks(current_date):
for task in frappe.get_all("Task", ["name"], {"status": "Open", "exp_end_date": ("<", current_date)}):
task = frappe.get_doc("Task", task.name)
task.status = "Completed"
task.save()
def make_project(current_date):
if not frappe.db.exists('Project',
"New Product Development " + current_date.strftime("%Y-%m-%d")):
project = frappe.get_doc({
"doctype": "Project",
"project_name": "New Product Development " + current_date.strftime("%Y-%m-%d"),
})
project.insert()

View File

@ -1,180 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import json
import random
import frappe
from frappe.desk import query_report
from frappe.utils.make_random import get_random, how_many
import erpnext
from erpnext.accounts.party import get_party_account_currency
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import (
make_supplier_quotation_from_rfq,
)
from erpnext.exceptions import InvalidCurrency
from erpnext.setup.utils import get_exchange_rate
from erpnext.stock.doctype.material_request.material_request import make_request_for_quotation
def work():
frappe.set_user(frappe.db.get_global('demo_purchase_user'))
if random.random() < 0.6:
report = "Items To Be Requested"
for row in query_report.run(report)["result"][:random.randint(1, 5)]:
item_code, qty = row[0], abs(row[-1])
mr = make_material_request(item_code, qty)
if random.random() < 0.6:
for mr in frappe.get_all('Material Request',
filters={'material_request_type': 'Purchase', 'status': 'Open'},
limit=random.randint(1,6)):
if not frappe.get_all('Request for Quotation',
filters={'material_request': mr.name}, limit=1):
rfq = make_request_for_quotation(mr.name)
rfq.transaction_date = frappe.flags.current_date
add_suppliers(rfq)
rfq.save()
rfq.submit()
# Make suppier quotation from RFQ against each supplier.
if random.random() < 0.6:
for rfq in frappe.get_all('Request for Quotation',
filters={'status': 'Open'}, limit=random.randint(1, 6)):
if not frappe.get_all('Supplier Quotation',
filters={'request_for_quotation': rfq.name}, limit=1):
rfq = frappe.get_doc('Request for Quotation', rfq.name)
for supplier in rfq.suppliers:
supplier_quotation = make_supplier_quotation_from_rfq(rfq.name, for_supplier=supplier.supplier)
supplier_quotation.save()
supplier_quotation.submit()
# get supplier details
supplier = get_random("Supplier")
company_currency = frappe.get_cached_value('Company', erpnext.get_default_company(), "default_currency")
party_account_currency = get_party_account_currency("Supplier", supplier, erpnext.get_default_company())
if company_currency == party_account_currency:
exchange_rate = 1
else:
exchange_rate = get_exchange_rate(party_account_currency, company_currency, args="for_buying")
# make supplier quotations
if random.random() < 0.5:
from erpnext.stock.doctype.material_request.material_request import make_supplier_quotation
report = "Material Requests for which Supplier Quotations are not created"
for row in query_report.run(report)["result"][:random.randint(1, 3)]:
if row[0] != "Total":
sq = frappe.get_doc(make_supplier_quotation(row[0]))
sq.transaction_date = frappe.flags.current_date
sq.supplier = supplier
sq.currency = party_account_currency or company_currency
sq.conversion_rate = exchange_rate
sq.insert()
sq.submit()
frappe.db.commit()
# make purchase orders
if random.random() < 0.5:
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
report = "Requested Items To Be Ordered"
for row in query_report.run(report)["result"][:how_many("Purchase Order")]:
if row[0] != "Total":
try:
po = frappe.get_doc(make_purchase_order(row[0]))
po.supplier = supplier
po.currency = party_account_currency or company_currency
po.conversion_rate = exchange_rate
po.transaction_date = frappe.flags.current_date
po.insert()
po.submit()
except Exception:
pass
else:
frappe.db.commit()
if random.random() < 0.5:
make_subcontract()
def make_material_request(item_code, qty):
mr = frappe.new_doc("Material Request")
variant_of = frappe.db.get_value('Item', item_code, 'variant_of') or item_code
if frappe.db.get_value('BOM', {'item': variant_of, 'is_default': 1, 'is_active': 1}):
mr.material_request_type = 'Manufacture'
else:
mr.material_request_type = "Purchase"
mr.transaction_date = frappe.flags.current_date
mr.schedule_date = frappe.utils.add_days(mr.transaction_date, 7)
mr.append("items", {
"doctype": "Material Request Item",
"schedule_date": frappe.utils.add_days(mr.transaction_date, 7),
"item_code": item_code,
"qty": qty
})
mr.insert()
mr.submit()
return mr
def add_suppliers(rfq):
for i in range(2):
supplier = get_random("Supplier")
if supplier not in [d.supplier for d in rfq.get('suppliers')]:
rfq.append("suppliers", { "supplier": supplier })
def make_subcontract():
from erpnext.buying.doctype.purchase_order.purchase_order import make_rm_stock_entry
item_code = get_random("Item", {"is_sub_contracted_item": 1})
if item_code:
# make sub-contract PO
po = frappe.new_doc("Purchase Order")
po.is_subcontracted = "Yes"
po.supplier = get_random("Supplier")
po.transaction_date = frappe.flags.current_date # added
po.schedule_date = frappe.utils.add_days(frappe.flags.current_date, 7)
item_code = get_random("Item", {"is_sub_contracted_item": 1})
po.append("items", {
"item_code": item_code,
"schedule_date": frappe.utils.add_days(frappe.flags.current_date, 7),
"qty": random.randint(10, 30)
})
po.set_missing_values()
try:
po.insert()
except InvalidCurrency:
return
po.submit()
# make material request for
make_material_request(po.items[0].item_code, po.items[0].qty)
# transfer material for sub-contract
rm_items = get_rm_item(po.items[0], po.supplied_items[0])
stock_entry = frappe.get_doc(make_rm_stock_entry(po.name, json.dumps([rm_items])))
stock_entry.from_warehouse = "Stores - WPL"
stock_entry.to_warehouse = "Supplier - WPL"
stock_entry.insert()
def get_rm_item(items, supplied_items):
return {
"item_code": items.get("item_code"),
"rm_item_code": supplied_items.get("rm_item_code"),
"item_name": supplied_items.get("rm_item_code"),
"qty": supplied_items.get("required_qty") + random.randint(3,10),
"amount": supplied_items.get("amount"),
"warehouse": supplied_items.get("reserve_warehouse"),
"rate": supplied_items.get("rate"),
"stock_uom": supplied_items.get("stock_uom")
}

View File

@ -1,145 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import random
import frappe
from frappe.utils import flt
from frappe.utils.make_random import add_random_children, get_random
import erpnext
from erpnext.accounts.doctype.payment_request.payment_request import (
make_payment_entry,
make_payment_request,
)
from erpnext.accounts.party import get_party_account_currency
from erpnext.setup.utils import get_exchange_rate
def work(domain="Manufacturing"):
frappe.set_user(frappe.db.get_global('demo_sales_user_2'))
for i in range(random.randint(1,7)):
if random.random() < 0.5:
make_opportunity(domain)
for i in range(random.randint(1,3)):
if random.random() < 0.5:
make_quotation(domain)
try:
lost_reason = frappe.get_doc({
"doctype": "Opportunity Lost Reason",
"lost_reason": "Did not ask"
})
lost_reason.save(ignore_permissions=True)
except frappe.exceptions.DuplicateEntryError:
pass
# lost quotations / inquiries
if random.random() < 0.3:
for i in range(random.randint(1,3)):
quotation = get_random('Quotation', doc=True)
if quotation and quotation.status == 'Submitted':
quotation.declare_order_lost([{'lost_reason': 'Did not ask'}])
for i in range(random.randint(1,3)):
opportunity = get_random('Opportunity', doc=True)
if opportunity and opportunity.status in ('Open', 'Replied'):
opportunity.declare_enquiry_lost([{'lost_reason': 'Did not ask'}])
for i in range(random.randint(1,3)):
if random.random() < 0.6:
make_sales_order()
if random.random() < 0.5:
#make payment request against Sales Order
sales_order_name = get_random("Sales Order", filters={"docstatus": 1})
try:
if sales_order_name:
so = frappe.get_doc("Sales Order", sales_order_name)
if flt(so.per_billed) != 100:
payment_request = make_payment_request(dt="Sales Order", dn=so.name, recipient_id=so.contact_email,
submit_doc=True, mute_email=True, use_dummy_message=True)
payment_entry = frappe.get_doc(make_payment_entry(payment_request.name))
payment_entry.posting_date = frappe.flags.current_date
payment_entry.submit()
except Exception:
pass
def make_opportunity(domain):
b = frappe.get_doc({
"doctype": "Opportunity",
"opportunity_from": "Customer",
"party_name": frappe.get_value("Customer", get_random("Customer"), 'name'),
"opportunity_type": "Sales",
"with_items": 1,
"transaction_date": frappe.flags.current_date,
})
add_random_children(b, "items", rows=4, randomize = {
"qty": (1, 5),
"item_code": ("Item", {"has_variants": 0, "is_fixed_asset": 0, "domain": domain})
}, unique="item_code")
b.insert()
frappe.db.commit()
def make_quotation(domain):
# get open opportunites
opportunity = get_random("Opportunity", {"status": "Open", "with_items": 1})
if opportunity:
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
qtn = frappe.get_doc(make_quotation(opportunity))
qtn.insert()
frappe.db.commit()
qtn.submit()
frappe.db.commit()
else:
# make new directly
# get customer, currency and exchange_rate
customer = get_random("Customer")
company_currency = frappe.get_cached_value('Company', erpnext.get_default_company(), "default_currency")
party_account_currency = get_party_account_currency("Customer", customer, erpnext.get_default_company())
if company_currency == party_account_currency:
exchange_rate = 1
else:
exchange_rate = get_exchange_rate(party_account_currency, company_currency, args="for_selling")
qtn = frappe.get_doc({
"creation": frappe.flags.current_date,
"doctype": "Quotation",
"quotation_to": "Customer",
"party_name": customer,
"currency": party_account_currency or company_currency,
"conversion_rate": exchange_rate,
"order_type": "Sales",
"transaction_date": frappe.flags.current_date,
})
add_random_children(qtn, "items", rows=3, randomize = {
"qty": (1, 5),
"item_code": ("Item", {"has_variants": "0", "is_fixed_asset": 0, "domain": domain})
}, unique="item_code")
qtn.insert()
frappe.db.commit()
qtn.submit()
frappe.db.commit()
def make_sales_order():
q = get_random("Quotation", {"status": "Submitted"})
if q:
from erpnext.selling.doctype.quotation.quotation import make_sales_order as mso
so = frappe.get_doc(mso(q))
so.transaction_date = frappe.flags.current_date
so.delivery_date = frappe.utils.add_days(frappe.flags.current_date, 10)
so.insert()
frappe.db.commit()
so.submit()
frappe.db.commit()

View File

@ -1,138 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import random
import frappe
from frappe.desk import query_report
import erpnext
from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return
from erpnext.stock.doctype.serial_no.serial_no import SerialNoQtyError, SerialNoRequiredError
from erpnext.stock.stock_ledger import NegativeStockError
def work():
frappe.set_user(frappe.db.get_global('demo_manufacturing_user'))
make_purchase_receipt()
make_delivery_note()
make_stock_reconciliation()
submit_draft_stock_entries()
make_sales_return_records()
make_purchase_return_records()
def make_purchase_receipt():
if random.random() < 0.6:
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
report = "Purchase Order Items To Be Received"
po_list =list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:random.randint(1, 10)]
for po in po_list:
pr = frappe.get_doc(make_purchase_receipt(po))
if pr.is_subcontracted=="Yes":
pr.supplier_warehouse = "Supplier - WPL"
pr.posting_date = frappe.flags.current_date
pr.insert()
try:
pr.submit()
except NegativeStockError:
print('Negative stock for {0}'.format(po))
pass
frappe.db.commit()
def make_delivery_note():
# make purchase requests
# make delivery notes (if possible)
if random.random() < 0.6:
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
report = "Ordered Items To Be Delivered"
for so in list(set([r[0] for r in query_report.run(report)["result"]
if r[0]!="Total"]))[:random.randint(1, 3)]:
dn = frappe.get_doc(make_delivery_note(so))
dn.posting_date = frappe.flags.current_date
for d in dn.get("items"):
if not d.expense_account:
d.expense_account = ("Cost of Goods Sold - {0}".format(
frappe.get_cached_value('Company', dn.company, 'abbr')))
try:
dn.insert()
dn.submit()
frappe.db.commit()
except (NegativeStockError, SerialNoRequiredError, SerialNoQtyError, UnableToSelectBatchError):
frappe.db.rollback()
def make_stock_reconciliation():
# random set some items as damaged
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
EmptyStockReconciliationItemsError,
OpeningEntryAccountError,
)
if random.random() < 0.4:
stock_reco = frappe.new_doc("Stock Reconciliation")
stock_reco.posting_date = frappe.flags.current_date
stock_reco.company = erpnext.get_default_company()
stock_reco.get_items_for("Stores - WPL")
if stock_reco.items:
for item in stock_reco.items:
if item.qty:
item.qty = item.qty - round(random.randint(1, item.qty))
try:
stock_reco.insert(ignore_permissions=True, ignore_mandatory=True)
stock_reco.submit()
frappe.db.commit()
except OpeningEntryAccountError:
frappe.db.rollback()
except EmptyStockReconciliationItemsError:
frappe.db.rollback()
def submit_draft_stock_entries():
from erpnext.stock.doctype.stock_entry.stock_entry import (
DuplicateEntryForWorkOrderError,
IncorrectValuationRateError,
OperationsNotCompleteError,
)
# try posting older drafts (if exists)
frappe.db.commit()
for st in frappe.db.get_values("Stock Entry", {"docstatus":0}, "name"):
try:
ste = frappe.get_doc("Stock Entry", st[0])
ste.posting_date = frappe.flags.current_date
ste.save()
ste.submit()
frappe.db.commit()
except (NegativeStockError, IncorrectValuationRateError, DuplicateEntryForWorkOrderError,
OperationsNotCompleteError):
frappe.db.rollback()
def make_sales_return_records():
if random.random() < 0.1:
for data in frappe.get_all('Delivery Note', fields=["name"], filters={"docstatus": 1}):
if random.random() < 0.1:
try:
dn = make_sales_return(data.name)
dn.insert()
dn.submit()
frappe.db.commit()
except Exception:
frappe.db.rollback()
def make_purchase_return_records():
if random.random() < 0.1:
for data in frappe.get_all('Purchase Receipt', fields=["name"], filters={"docstatus": 1}):
if random.random() < 0.1:
try:
pr = make_purchase_return(data.name)
pr.insert()
pr.submit()
frappe.db.commit()
except Exception:
frappe.db.rollback()

View File

@ -1,35 +0,0 @@
data = {
'desktop_icons': [
'Restaurant',
'Hotels',
'Accounts',
'Buying',
'Stock',
'HR',
'Project',
'ToDo'
],
'restricted_roles': [
'Restaurant Manager',
'Hotel Manager',
'Hotel Reservation User'
],
'custom_fields': {
'Sales Invoice': [
{
'fieldname': 'restaurant', 'fieldtype': 'Link', 'options': 'Restaurant',
'insert_after': 'customer_name', 'label': 'Restaurant',
},
{
'fieldname': 'restaurant_table', 'fieldtype': 'Link', 'options': 'Restaurant Table',
'insert_after': 'restaurant', 'label': 'Restaurant Table',
}
],
'Price List': [
{
'fieldname':'restaurant_menu', 'fieldtype':'Link', 'options':'Restaurant Menu', 'label':'Restaurant Menu',
'insert_after':'currency'
}
]
}
}

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