Shareholder and related doctypes (#12215)
* Added doctypes related to shares
- Shareholder doctype, the entity who does the transaction
- Share doctype, a child table to kepp list of all shares
- Share Transfer doctype, Logs of all made transactions
* Added logic for share transfer
- update shareholder automatically on share transfer/issue/purchase
- purchase and transfer have method remove_share which doesn't get executed because of some bug
* Added report view for share ledger
* Removed share number tracking
- removed share number tracking from Share Ledger child table for Shareholder doctype
- new doctype Share Type created
* Share Balance report added
- math behind Share Balance report needs fixing
* Changes in shareholder
- Share numbers are not tracked in Shareholder
- Share Ledger doctype deleted
- Share Balance Report bug fixed
* Shareholder
- fixed report for share balance
- remove bug from share transfer by making share type mandatory
- added buttons to shareholder for direct link to report
* Added tests for Share Transfer
* minor codacy fixes
* Added Shareholder to Party Type
* rate in share ledger & balance report is currency
* First attempt at modelling shares after Item
* Share Manager changes
- on creation of shareholder party it should check if the same combo exists
- in shareholder party make report button visible iff folio no exists
- create folio no on creation of share transfer iff it doesnt already exist
- move reports from shareholder to shareholder party
* Shareholder chages
- delete share child table doctype
- autoname added to folio no
- modify tests for share transfer
* minor changes
* removed share_no child doctype
* Restructured Share Transfer Logic (not tested)
- Everything revolves around Share Balance child table in Shareholder Party
- is_company flag still to be utilized
* Tested Share Tranfer
* minor fixes
* started new shareholder structure
* modified shareholder
* renamed Shareholder Party to Shareholder
* Shareholder rewrite complete
* new tests for shareholder written
* codacy fixes
* Added documentation
diff --git a/erpnext/accounts/doctype/share_balance/__init__.py b/erpnext/accounts/doctype/share_balance/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/share_balance/__init__.py
diff --git a/erpnext/accounts/doctype/share_balance/share_balance.json b/erpnext/accounts/doctype/share_balance/share_balance.json
new file mode 100644
index 0000000..04d7bb7
--- /dev/null
+++ b/erpnext/accounts/doctype/share_balance/share_balance.json
@@ -0,0 +1,342 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2018-01-10 13:03:35.544736",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "share_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Share Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Share Type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "from_no",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From No",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "rate",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "no_of_shares",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "No of Shares",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "to_no",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "To No",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "amount",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_8",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "fieldname": "is_company",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Company",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "current_state",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Current State",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nIssued\nPurchased",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2018-01-10 18:32:36.201124",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Share Balance",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/share_balance/share_balance.py b/erpnext/accounts/doctype/share_balance/share_balance.py
new file mode 100644
index 0000000..bd165cd
--- /dev/null
+++ b/erpnext/accounts/doctype/share_balance/share_balance.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class ShareBalance(Document):
+ pass
diff --git a/erpnext/accounts/doctype/share_transfer/__init__.py b/erpnext/accounts/doctype/share_transfer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/__init__.py
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.js b/erpnext/accounts/doctype/share_transfer/share_transfer.js
new file mode 100644
index 0000000..fbf12e5
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.js
@@ -0,0 +1,35 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.provide("erpnext.share_transfer");
+
+frappe.ui.form.on('Share Transfer', {
+ refresh: function(frm) {
+ // Don't show Parties which are a Company
+ let shareholders = ['from_shareholder', 'to_shareholder'];
+ shareholders.forEach((shareholder) => {
+ frm.fields_dict[shareholder].get_query = function() {
+ return {
+ filters: [
+ ["Shareholder", "is_company", "=", 0]
+ ]
+ };
+ };
+ });
+ },
+ no_of_shares: (frm) => {
+ if (frm.doc.rate != undefined || frm.doc.rate != null){
+ erpnext.share_transfer.update_amount(frm);
+ }
+ },
+ rate: (frm) => {
+ if (frm.doc.no_of_shares != undefined || frm.doc.no_of_shares != null){
+ erpnext.share_transfer.update_amount(frm);
+ }
+ }
+});
+
+erpnext.share_transfer.update_amount = function(frm) {
+ frm.doc.amount = frm.doc.no_of_shares * frm.doc.rate;
+ frm.refresh_field("amount");
+};
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.json b/erpnext/accounts/doctype/share_transfer/share_transfer.json
new file mode 100644
index 0000000..9e6f49d
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.json
@@ -0,0 +1,699 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "ST.######",
+ "beta": 0,
+ "creation": "2017-12-25 17:18:03.143726",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "transfer_type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Transfer Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nIssue\nPurchase\nTransfer",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_1",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_1",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.transfer_type != 'Issue'",
+ "fieldname": "from_shareholder",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From Shareholder",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Shareholder",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.transfer_type != 'Issue'",
+ "fieldname": "from_folio_no",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From Folio No",
+ "length": 0,
+ "no_copy": 0,
+ "options": "from_shareholder.folio_no",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.transfer_type != 'Purchase'",
+ "fieldname": "to_shareholder",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "To Shareholder",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Shareholder",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.transfer_type != 'Purchase'",
+ "fieldname": "to_folio_no",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "To Folio No",
+ "length": 0,
+ "no_copy": 0,
+ "options": "to_shareholder.folio_no",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "share_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Share Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Share Type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "(including)",
+ "fieldname": "from_no",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From No",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_8",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "no_of_shares",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "No of Shares",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "(including)",
+ "fieldname": "to_no",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "To No",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_11",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Company",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "remarks",
+ "fieldtype": "Long Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Remarks",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2018-01-23 16:12:54.776896",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Share Transfer",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.py b/erpnext/accounts/doctype/share_transfer/share_transfer.py
new file mode 100644
index 0000000..c2eaa03
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.py
@@ -0,0 +1,271 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe.model.naming import make_autoname
+
+class ShareTransfer(Document):
+ def before_save(self):
+ if self.transfer_type == 'Issue':
+ company_doc = self.get_shareholder_doc(self.company)
+ company_doc.append('share_balance', {
+ 'share_type': self.share_type,
+ 'from_no': self.from_no,
+ 'to_no': self.to_no,
+ 'rate': self.rate,
+ 'amount': self.amount,
+ 'no_of_shares': self.no_of_shares,
+ 'is_company': 1,
+ 'current_state': 'Issued'
+ })
+ company_doc.save()
+
+ doc = frappe.get_doc('Shareholder', self.to_shareholder)
+ doc.append('share_balance', {
+ 'share_type': self.share_type,
+ 'from_no': self.from_no,
+ 'to_no': self.to_no,
+ 'rate': self.rate,
+ 'amount': self.amount,
+ 'no_of_shares': self.no_of_shares
+ })
+ doc.save()
+
+ elif self.transfer_type == 'Purchase':
+ self.remove_shares(self.from_shareholder)
+ self.remove_shares(self.get_shareholder_doc(self.company).name)
+
+ elif self.transfer_type == 'Transfer':
+ self.remove_shares(self.from_shareholder)
+ doc = frappe.get_doc('Shareholder', self.to_shareholder)
+ doc.append('share_balance', {
+ 'share_type': self.share_type,
+ 'from_no': self.from_no,
+ 'to_no': self.to_no,
+ 'rate': self.rate,
+ 'amount': self.amount,
+ 'no_of_shares': self.no_of_shares
+ })
+ doc.save()
+
+ def validate(self):
+ self.basic_validations()
+ self.folio_no_validation()
+ if self.transfer_type == 'Issue':
+ if not self.get_shareholder_doc(self.company):
+ company_doc = frappe.get_doc({
+ 'doctype': 'Shareholder',
+ 'title': self.company,
+ 'company': self.company,
+ 'is_company': 1
+ })
+ company_doc.insert()
+ # validate share doesnt exist in company
+ ret_val = self.share_exists(self.get_shareholder_doc(self.company).name)
+ if ret_val != False:
+ frappe.throw('The shares already exist')
+ else:
+ # validate share exists with from_shareholder
+ ret_val = self.share_exists(self.from_shareholder)
+ if ret_val != True:
+ frappe.throw('The shares don\'t exist with the {0}'.format(self.from_shareholder))
+
+ def basic_validations(self):
+ if self.transfer_type == 'Purchase':
+ self.to_shareholder = ''
+ if self.from_shareholder is None or self.from_shareholder is '':
+ frappe.throw('The field \'From Shareholder\' cannot be blank')
+ if self.from_folio_no is None or self.from_folio_no is '':
+ self.to_folio_no = self.autoname_folio(self.to_shareholder)
+ elif (self.transfer_type == 'Issue'):
+ self.from_shareholder = ''
+ if self.to_shareholder is None or self.to_shareholder == '':
+ frappe.throw('The field \'To Shareholder\' cannot be blank')
+ if self.to_folio_no is None or self.to_folio_no is '':
+ self.to_folio_no = self.autoname_folio(self.to_shareholder)
+ else:
+ if self.from_shareholder is None or self.to_shareholder is None:
+ frappe.throw('The fields \'From Shareholder\' and \'To Shareholder\' cannot be blank')
+ if self.to_folio_no is None or self.to_folio_no is '':
+ self.to_folio_no = self.autoname_folio(self.to_shareholder)
+ if self.from_shareholder == self.to_shareholder:
+ frappe.throw('The seller and the buyer cannot be the same')
+ if self.no_of_shares != self.to_no - self.from_no + 1:
+ frappe.throw('The number of shares and the share numbers are inconsistent!')
+ if self.amount is None:
+ self.amount = self.rate * self.no_of_shares
+ if self.amount != self.rate * self.no_of_shares:
+ frappe.throw('There\'s inconsistency between the rate, no of shares and the amount calculated')
+
+ def share_exists(self, shareholder):
+ # return True if exits,
+ # False if completely doesn't exist,
+ # 'partially exists' if partailly doesn't exist
+ ret_val = self.recursive_share_check(shareholder, self.share_type,
+ query = {
+ 'from_no': self.from_no,
+ 'to_no': self.to_no
+ }
+ )
+ if all(boolean == True for boolean in ret_val):
+ return True
+ elif True in ret_val:
+ return 'partially exists'
+ else:
+ return False
+
+ def recursive_share_check(self, shareholder, share_type, query):
+ # query = {'from_no': share_starting_no, 'to_no': share_ending_no}
+ # Recursive check if a given part of shares is held by the shareholder
+ # return a list containing True and False
+ # Eg. [True, False, True]
+ # All True implies its completely inside
+ # All False implies its completely outside
+ # A mix implies its partially inside/outside
+ does_share_exist = []
+ doc = frappe.get_doc('Shareholder', shareholder)
+ for entry in doc.share_balance:
+ if entry.share_type != share_type or \
+ entry.from_no > query['to_no'] or \
+ entry.to_no < query['from_no']:
+ continue # since query lies outside bounds
+ elif entry.from_no <= query['from_no'] and entry.to_no >= query['to_no']:
+ return [True] # absolute truth!
+ elif entry.from_no >= query['from_no'] and entry.to_no <= query['to_no']:
+ # split and check
+ does_share_exist.extend(self.recursive_share_check(shareholder,
+ share_type,
+ {
+ 'from_no': query['from_no'],
+ 'to_no': entry.from_no - 1
+ }
+ ))
+ does_share_exist.append(True)
+ does_share_exist.extend(self.recursive_share_check(shareholder,
+ share_type,
+ {
+ 'from_no': entry.to_no + 1,
+ 'to_no': query['to_no']
+ }
+ ))
+ elif query['from_no'] <= entry.from_no <= query['to_no'] and entry.to_no >= query['to_no']:
+ does_share_exist.extend(self.recursive_share_check(shareholder,
+ share_type,
+ {
+ 'from_no': query['from_no'],
+ 'to_no': entry.from_no - 1
+ }
+ ))
+ elif query['from_no'] <= entry.to_no <= query['to_no'] and entry.from_no <= query['from_no']:
+ does_share_exist.extend(self.recursive_share_check(shareholder,
+ share_type,
+ {
+ 'from_no': entry.to_no + 1,
+ 'to_no': query['to_no']
+ }
+ ))
+
+ does_share_exist.append(False)
+ return does_share_exist
+
+ def folio_no_validation(self):
+ shareholders = ['from_shareholder', 'to_shareholder']
+ shareholders = [shareholder for shareholder in shareholders if self.get(shareholder) is not '']
+ for shareholder in shareholders:
+ doc = frappe.get_doc('Shareholder', self.get(shareholder))
+ if doc.company != self.company:
+ frappe.throw('The shareholder doesn\'t belong to this company')
+ if doc.folio_no is '' or doc.folio_no is None:
+ doc.folio_no = self.from_folio_no if (shareholder == 'from_shareholder') else self.to_folio_no;
+ doc.save()
+ else:
+ if doc.folio_no != (self.from_folio_no if (shareholder == 'from_shareholder') else self.to_folio_no):
+ frappe.throw('The folio numbers are not matching')
+
+ def autoname_folio(self, shareholder, is_company=False):
+ if is_company:
+ doc = self.get_shareholder_doc(shareholder)
+ else:
+ doc = frappe.get_doc('Shareholder' , shareholder)
+ doc.folio_no = make_autoname('FN.#####')
+ doc.save()
+ return doc.folio_no
+
+ def remove_shares(self, shareholder):
+ self.iterative_share_removal(shareholder, self.share_type,
+ {
+ 'from_no': self.from_no,
+ 'to_no' : self.to_no
+ },
+ rate = self.rate,
+ amount = self.amount
+ )
+
+ def iterative_share_removal(self, shareholder, share_type, query, rate, amount):
+ # query = {'from_no': share_starting_no, 'to_no': share_ending_no}
+ # Shares exist for sure
+ # Iterate over all entries and modify entry if in entry
+ doc = frappe.get_doc('Shareholder', shareholder)
+ current_entries = doc.share_balance
+ new_entries = []
+
+ for entry in current_entries:
+ # use spaceage logic here
+ if entry.share_type != share_type or \
+ entry.from_no > query['to_no'] or \
+ entry.to_no < query['from_no']:
+ new_entries.append(entry)
+ continue # since query lies outside bounds
+ elif entry.from_no <= query['from_no'] and entry.to_no >= query['to_no']:
+ #split
+ if entry.from_no == query['from_no']:
+ if entry.to_no == query['to_no']:
+ pass #nothing to append
+ else:
+ new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate))
+ else:
+ if entry.to_no == query['to_no']:
+ new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate))
+ else:
+ new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate))
+ new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate))
+ elif entry.from_no >= query['from_no'] and entry.to_no <= query['to_no']:
+ # split and check
+ pass #nothing to append
+ elif query['from_no'] <= entry.from_no <= query['to_no'] and entry.to_no >= query['to_no']:
+ new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate))
+ elif query['from_no'] <= entry.to_no <= query['to_no'] and entry.from_no <= query['from_no']:
+ new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate))
+ else:
+ new_entries.append(entry)
+
+ doc.share_balance = []
+ for entry in new_entries:
+ doc.append('share_balance', entry)
+ doc.save()
+
+ def return_share_balance_entry(self, from_no, to_no, rate):
+ # return an entry as a dict
+ return {
+ 'share_type' : self.share_type,
+ 'from_no' : from_no,
+ 'to_no' : to_no,
+ 'rate' : rate,
+ 'amount' : self.rate * (to_no - from_no + 1),
+ 'no_of_shares' : to_no - from_no + 1
+ }
+
+ def get_shareholder_doc(self, shareholder):
+ # Get Shareholder doc based on the Shareholder title
+ doc = frappe.get_list('Shareholder',
+ filters = [
+ ('Shareholder', 'title', '=', shareholder)
+ ]
+ )
+ if len(doc) == 1:
+ return frappe.get_doc('Shareholder', doc[0]['name'])
+ else: #It will necessarily by 0 indicating it doesn't exist
+ return False
diff --git a/erpnext/accounts/doctype/share_transfer/test_records.json b/erpnext/accounts/doctype/share_transfer/test_records.json
new file mode 100644
index 0000000..13b37b4
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/test_records.json
@@ -0,0 +1,76 @@
+
+[
+ {
+ "doctype" : "Company",
+ "company_name" : "Stark Tower",
+ "abbr" : "ST",
+ "default_currency" : "INR"
+ },
+ {
+ "doctype" : "Share Type",
+ "title" : "Class A"
+ },
+ {
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Issue",
+ "date" : "2018-01-01",
+ "to_shareholder" : "SH-00001",
+ "share_type" : "Equity",
+ "from_no" : 1,
+ "to_no" : 500,
+ "no_of_shares" : 500,
+ "rate" : 10,
+ "company" : "Stark Tower"
+ },
+ {
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Transfer",
+ "date" : "2018-01-02",
+ "from_shareholder" : "SH-00001",
+ "to_shareholder" : "SH-00002",
+ "share_type" : "Equity",
+ "from_no" : 101,
+ "to_no" : 200,
+ "no_of_shares" : 100,
+ "rate" : 15,
+ "company" : "Stark Tower"
+ },
+ {
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Transfer",
+ "date" : "2018-01-03",
+ "from_shareholder" : "SH-00001",
+ "to_shareholder" : "SH-00003",
+ "share_type" : "Equity",
+ "from_no" : 201,
+ "to_no" : 500,
+ "no_of_shares" : 300,
+ "rate" : 20,
+ "company" : "Stark Tower"
+ },
+ {
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Transfer",
+ "date" : "2018-01-04",
+ "from_shareholder" : "SH-00001",
+ "to_shareholder" : "SH-00002",
+ "share_type" : "Equity",
+ "from_no" : 201,
+ "to_no" : 400,
+ "no_of_shares" : 200,
+ "rate" : 15,
+ "company" : "Stark Tower"
+ },
+ {
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Purchase",
+ "date" : "2018-01-05",
+ "from_shareholder" : "SH-00003",
+ "share_type" : "Equity",
+ "from_no" : 401,
+ "to_no" : 500,
+ "no_of_shares" : 100,
+ "rate" : 25,
+ "company" : "Stark Tower"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/share_transfer/test_share_transfer.js b/erpnext/accounts/doctype/share_transfer/test_share_transfer.js
new file mode 100644
index 0000000..e5530fa
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/test_share_transfer.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Share Transfer", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Share Transfer
+ () => frappe.tests.make('Share Transfer', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/share_transfer/test_share_transfer.py b/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
new file mode 100644
index 0000000..8dee358
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from frappe import ValidationError
+
+test_dependencies = ["Share Type", "Shareholder"]
+
+class TestShareTransfer(unittest.TestCase):
+ def test_invalid_share_transfer(self):
+ doc = frappe.get_doc({
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Transfer",
+ "date" : "2018-01-05",
+ "from_shareholder" : "SH-00003",
+ "to_shareholder" : "SH-00002",
+ "share_type" : "Equity",
+ "from_no" : 1,
+ "to_no" : 100,
+ "no_of_shares" : 100,
+ "rate" : 15,
+ "company" : "Stark Tower"
+ })
+ self.assertRaises(ValidationError, doc.insert)
+
+ def test_invalid_share_purchase(self):
+ doc = frappe.get_doc({
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Purchase",
+ "date" : "2018-01-02",
+ "from_shareholder" : "SH-00001",
+ "share_type" : "Equity",
+ "from_no" : 1,
+ "to_no" : 200,
+ "no_of_shares" : 200,
+ "rate" : 15,
+ "company" : "Stark Tower"
+ })
+ self.assertRaises(ValidationError, doc.insert)
diff --git a/erpnext/accounts/doctype/share_type/__init__.py b/erpnext/accounts/doctype/share_type/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/__init__.py
diff --git a/erpnext/accounts/doctype/share_type/share_type.js b/erpnext/accounts/doctype/share_type/share_type.js
new file mode 100644
index 0000000..1ae85e3
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/share_type.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Share Type', {
+ refresh: function(frm) {
+
+ }
+});
diff --git a/erpnext/accounts/doctype/share_type/share_type.json b/erpnext/accounts/doctype/share_type/share_type.json
new file mode 100644
index 0000000..3373e03
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/share_type.json
@@ -0,0 +1,123 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:title",
+ "beta": 0,
+ "creation": "2017-12-28 14:55:20.950877",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Title",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "description",
+ "fieldtype": "Long Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Description",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-12-28 14:55:20.950877",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Share Type",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/share_type/share_type.py b/erpnext/accounts/doctype/share_type/share_type.py
new file mode 100644
index 0000000..ab4b8bc
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/share_type.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class ShareType(Document):
+ pass
diff --git a/erpnext/accounts/doctype/share_type/test_records.json b/erpnext/accounts/doctype/share_type/test_records.json
new file mode 100644
index 0000000..88cb8c0
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/test_records.json
@@ -0,0 +1,10 @@
+[
+ {
+ "doctype": "Share Type",
+ "title": "Class A"
+ },
+ {
+ "doctype": "Share Type",
+ "title": "Class B"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/share_type/test_share_type.js b/erpnext/accounts/doctype/share_type/test_share_type.js
new file mode 100644
index 0000000..620afa2
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/test_share_type.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Share Type", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Share Type
+ () => frappe.tests.make('Share Type', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/share_type/test_share_type.py b/erpnext/accounts/doctype/share_type/test_share_type.py
new file mode 100644
index 0000000..1c1f051
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/test_share_type.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestShareType(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/doctype/shareholder/__init__.py b/erpnext/accounts/doctype/shareholder/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/__init__.py
diff --git a/erpnext/accounts/doctype/shareholder/shareholder.js b/erpnext/accounts/doctype/shareholder/shareholder.js
new file mode 100644
index 0000000..c6f101e
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/shareholder.js
@@ -0,0 +1,54 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Shareholder', {
+ refresh: function(frm) {
+ frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Shareholder' };
+
+ frm.toggle_display(['contact_html'], !frm.doc.__islocal);
+
+ if (frm.doc.__islocal) {
+ hide_field(['contact_html']);
+ frappe.contacts.clear_address_and_contact(frm);
+ }
+ else {
+ if (frm.doc.is_company){
+ hide_field(['company']);
+ } else {
+ unhide_field(['contact_html']);
+ frappe.contacts.render_address_and_contact(frm);
+ }
+ }
+
+ if (frm.doc.folio_no != undefined){
+ frm.add_custom_button(__("Share Balance"), function(){
+ frappe.route_options = {
+ "shareholder": frm.doc.name,
+ };
+ frappe.set_route("query-report", "Share Balance");
+ });
+ frm.add_custom_button(__("Share Ledger"), function(){
+ frappe.route_options = {
+ "shareholder": frm.doc.name,
+ };
+ frappe.set_route("query-report", "Share Ledger");
+ });
+ let fields = ['title', 'folio_no', 'company'];
+ fields.forEach((fieldname) => {
+ frm.fields_dict[fieldname].df.read_only = 1;
+ frm.refresh_fields(fieldname);
+ });
+ $(`.btn:contains("New Contact"):visible`).hide();
+ $(`.btn:contains("Edit"):visible`).hide();
+ }
+ },
+ validate: (frm) => {
+ let contact_list = {
+ contacts: []
+ };
+ $('div[data-fieldname=contact_html] > .address-box').each( (index, ele) => {
+ contact_list.contacts.push(ele.innerText.replace(' Edit', ''));
+ });
+ frm.doc.contact_list = JSON.stringify(contact_list);
+ }
+});
diff --git a/erpnext/accounts/doctype/shareholder/shareholder.json b/erpnext/accounts/doctype/shareholder/shareholder.json
new file mode 100644
index 0000000..be35e7b
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/shareholder.json
@@ -0,0 +1,518 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "naming_series:",
+ "beta": 0,
+ "creation": "2017-12-25 16:50:53.878430",
+ "custom": 0,
+ "description": "",
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Title",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 1
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Series",
+ "length": 0,
+ "no_copy": 0,
+ "options": "SH-",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_2",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "folio_no",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Folio no.",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 1
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Company",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "is_company",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Company",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "address_contacts",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Address and Contacts",
+ "length": 0,
+ "no_copy": 0,
+ "options": "fa fa-map-marker",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "address_html",
+ "fieldtype": "HTML",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Address HTML",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "contact_html",
+ "fieldtype": "HTML",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Contact HTML",
+ "length": 0,
+ "no_copy": 0,
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_3",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Share Balance",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "share_balance",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Share Balance",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Share Balance",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "Hidden list maintaining the list of contacts linked to Shareholder",
+ "fieldname": "contact_list",
+ "fieldtype": "Code",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Contact List",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2018-01-23 17:49:02.941363",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Shareholder",
+ "name_case": "Title Case",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "search_fields": "folio_no",
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "title",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/shareholder/shareholder.py b/erpnext/accounts/doctype/shareholder/shareholder.py
new file mode 100644
index 0000000..c507fcf
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/shareholder.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
+
+class Shareholder(Document):
+ def onload(self):
+ """Load address and contacts in `__onload`"""
+ load_address_and_contact(self)
+
+ def on_trash(self):
+ delete_contact_and_address('Shareholder', self.name)
+
+ def before_save(self):
+ for entry in self.share_balance:
+ entry.amount = entry.no_of_shares * entry.rate
diff --git a/erpnext/accounts/doctype/shareholder/test_records.json b/erpnext/accounts/doctype/shareholder/test_records.json
new file mode 100644
index 0000000..ca289cb
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/test_records.json
@@ -0,0 +1,17 @@
+[
+ {
+ "doctype": "Shareholder",
+ "series": "SH-",
+ "title": "Iron Man"
+ },
+ {
+ "doctype": "Shareholder",
+ "series": "SH-",
+ "title": "Thor"
+ },
+ {
+ "doctype": "Shareholder",
+ "series": "SH-",
+ "title": "Hulk"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/shareholder/test_shareholder.js b/erpnext/accounts/doctype/shareholder/test_shareholder.js
new file mode 100644
index 0000000..61c5312
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/test_shareholder.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Shareholder", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Shareholder
+ () => frappe.tests.make('Shareholder', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/shareholder/test_shareholder.py b/erpnext/accounts/doctype/shareholder/test_shareholder.py
new file mode 100644
index 0000000..9ce0093
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/test_shareholder.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestShareholder(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/report/share_balance/__init__.py b/erpnext/accounts/report/share_balance/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/report/share_balance/__init__.py
diff --git a/erpnext/accounts/report/share_balance/share_balance.js b/erpnext/accounts/report/share_balance/share_balance.js
new file mode 100644
index 0000000..6db5bdd
--- /dev/null
+++ b/erpnext/accounts/report/share_balance/share_balance.js
@@ -0,0 +1,22 @@
+// -*- coding: utf-8 -*-
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Share Balance"] = {
+ "filters": [
+ {
+ "fieldname":"date",
+ "label": __("Date"),
+ "fieldtype": "Date",
+ "default": frappe.datetime.get_today(),
+ "reqd": 1
+ },
+ {
+ "fieldname":"shareholder",
+ "label": __("Shareholder"),
+ "fieldtype": "Link",
+ "options": "Shareholder"
+ }
+ ]
+}
diff --git a/erpnext/accounts/report/share_balance/share_balance.json b/erpnext/accounts/report/share_balance/share_balance.json
new file mode 100644
index 0000000..5477c09
--- /dev/null
+++ b/erpnext/accounts/report/share_balance/share_balance.json
@@ -0,0 +1,23 @@
+{
+ "add_total_row": 0,
+ "apply_user_permissions": 1,
+ "creation": "2017-12-28 16:12:34.154787",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2017-12-28 16:12:37.155649",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Share Balance",
+ "owner": "Administrator",
+ "ref_doctype": "Share Transfer",
+ "report_name": "Share Balance",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "System Manager"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/share_balance/share_balance.py b/erpnext/accounts/report/share_balance/share_balance.py
new file mode 100644
index 0000000..9f22f81
--- /dev/null
+++ b/erpnext/accounts/report/share_balance/share_balance.py
@@ -0,0 +1,58 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import msgprint, _
+
+def execute(filters=None):
+ if not filters: filters = {}
+
+ if not filters.get("date"):
+ frappe.throw(_("Please select date"))
+
+ columns = get_columns(filters)
+
+ date = filters.get("date")
+
+ data = []
+
+ if not filters.get("shareholder"):
+ pass
+ else:
+ share_type, no_of_shares, rate, amount = 1, 2, 3, 4
+
+ all_shares = get_all_shares(filters.get("shareholder"))
+ for share_entry in all_shares:
+ row = False
+ for datum in data:
+ if datum[share_type] == share_entry.share_type:
+ datum[no_of_shares] += share_entry.no_of_shares
+ datum[amount] += share_entry.amount
+ if datum[no_of_shares] == 0:
+ datum[rate] = 0
+ else:
+ datum[rate] = datum[amount] / datum[no_of_shares]
+ row = True
+ break
+ # new entry
+ if not row:
+ row = [filters.get("shareholder"),
+ share_entry.share_type, share_entry.no_of_shares, share_entry.rate, share_entry.amount]
+
+ data.append(row)
+
+ return columns, data
+
+def get_columns(filters):
+ columns = [
+ _("Shareholder") + ":Link/Shareholder:150",
+ _("Share Type") + "::90",
+ _("No of Shares") + "::90",
+ _("Average Rate") + ":Currency:90",
+ _("Amount") + ":Currency:90"
+ ]
+ return columns
+
+def get_all_shares(shareholder):
+ return frappe.get_doc('Shareholder', shareholder).share_balance
diff --git a/erpnext/accounts/report/share_ledger/__init__.py b/erpnext/accounts/report/share_ledger/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/report/share_ledger/__init__.py
diff --git a/erpnext/accounts/report/share_ledger/share_ledger.js b/erpnext/accounts/report/share_ledger/share_ledger.js
new file mode 100644
index 0000000..6d1c44a
--- /dev/null
+++ b/erpnext/accounts/report/share_ledger/share_ledger.js
@@ -0,0 +1,22 @@
+// -*- coding: utf-8 -*-
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Share Ledger"] = {
+ "filters": [
+ {
+ "fieldname":"date",
+ "label": __("Date"),
+ "fieldtype": "Date",
+ "default": frappe.datetime.get_today(),
+ "reqd": 1
+ },
+ {
+ "fieldname":"shareholder",
+ "label": __("Shareholder"),
+ "fieldtype": "Link",
+ "options": "Shareholder"
+ }
+ ]
+};
diff --git a/erpnext/accounts/report/share_ledger/share_ledger.json b/erpnext/accounts/report/share_ledger/share_ledger.json
new file mode 100644
index 0000000..d374bb7
--- /dev/null
+++ b/erpnext/accounts/report/share_ledger/share_ledger.json
@@ -0,0 +1,23 @@
+{
+ "add_total_row": 0,
+ "apply_user_permissions": 1,
+ "creation": "2017-12-27 16:15:52.615453",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2017-12-27 16:46:54.422356",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Share Ledger",
+ "owner": "Administrator",
+ "ref_doctype": "Share Transfer",
+ "report_name": "Share Ledger",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Administrator"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/share_ledger/share_ledger.py b/erpnext/accounts/report/share_ledger/share_ledger.py
new file mode 100644
index 0000000..3ed3c91
--- /dev/null
+++ b/erpnext/accounts/report/share_ledger/share_ledger.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import cstr, cint, getdate
+from frappe import msgprint, _
+
+def execute(filters=None):
+ if not filters: filters = {}
+
+ if not filters.get("date"):
+ frappe.throw(_("Please select date"))
+
+ columns = get_columns(filters)
+
+ date = filters.get("date")
+
+ data = []
+
+ if not filters.get("shareholder"):
+ pass
+ else:
+ transfers = get_all_transfers(date, filters.get("shareholder"))
+ for transfer in transfers:
+ if transfer.transfer_type == 'Transfer':
+ if transfer.from_shareholder == filters.get("shareholder"):
+ transfer.transfer_type += ' to {}'.format(transfer.to_shareholder)
+ else:
+ transfer.transfer_type += ' from {}'.format(transfer.from_shareholder)
+ row = [filters.get("shareholder"), transfer.date, transfer.transfer_type,
+ transfer.share_type, transfer.no_of_shares, transfer.rate, transfer.amount,
+ transfer.company, transfer.name]
+
+ data.append(row)
+
+ return columns, data
+
+def get_columns(filters):
+ columns = [
+ _("Shareholder") + ":Link/Shareholder:150",
+ _("Date") + ":Date:100",
+ _("Transfer Type") + "::140",
+ _("Share Type") + "::90",
+ _("No of Shares") + "::90",
+ _("Rate") + ":Currency:90",
+ _("Amount") + ":Currency:90",
+ _("Company") + "::150",
+ _("Share Transfer") + ":Link/Share Transfer:90"
+ ]
+ return columns
+
+def get_all_transfers(date, shareholder):
+ condition = ' '
+ # if company:
+ # condition = 'AND company = %(company)s '
+ return frappe.db.sql("""SELECT * FROM `tabShare Transfer`
+ WHERE (DATE(date) <= %(date)s AND from_shareholder = %(shareholder)s {condition})
+ OR (DATE(date) <= %(date)s AND to_shareholder = %(shareholder)s {condition})
+ ORDER BY date""".format(condition=condition),
+ {'date': date, 'shareholder': shareholder}, as_dict=1)
diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py
index 9a71608..088a119 100644
--- a/erpnext/config/accounts.py
+++ b/erpnext/config/accounts.py
@@ -457,6 +457,34 @@
]
},
{
+ "label": _("Share Management"),
+ "icon": "fa fa-microchip ",
+ "items": [
+ {
+ "type": "doctype",
+ "name":"Shareholder",
+ "description": _("List of available Shareholders with folio numbers")
+ },
+ {
+ "type": "doctype",
+ "name":"Share Transfer",
+ "description": _("List of all share transactions"),
+ },
+ {
+ "type": "report",
+ "name": "Share Ledger",
+ "doctype": "Share Transfer",
+ "is_query_report": True
+ },
+ {
+ "type": "report",
+ "name": "Share Balance",
+ "doctype": "Share Transfer",
+ "is_query_report": True
+ }
+ ]
+ },
+ {
"label": _("Help"),
"icon": "fa fa-facetime-video",
"items": [
diff --git a/erpnext/docs/assets/img/accounts/shareholder/sharebalance_1.png b/erpnext/docs/assets/img/accounts/shareholder/sharebalance_1.png
new file mode 100644
index 0000000..3e22324
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/sharebalance_1.png
Binary files differ
diff --git a/erpnext/docs/assets/img/accounts/shareholder/sharebalance_2.png b/erpnext/docs/assets/img/accounts/shareholder/sharebalance_2.png
new file mode 100644
index 0000000..c33cae8
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/sharebalance_2.png
Binary files differ
diff --git a/erpnext/docs/assets/img/accounts/shareholder/shareholder_tonystark.png b/erpnext/docs/assets/img/accounts/shareholder/shareholder_tonystark.png
new file mode 100644
index 0000000..ee05b20
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/shareholder_tonystark.png
Binary files differ
diff --git a/erpnext/docs/assets/img/accounts/shareholder/shareledger_1.png b/erpnext/docs/assets/img/accounts/shareholder/shareledger_1.png
new file mode 100644
index 0000000..a7cae31
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/shareledger_1.png
Binary files differ
diff --git a/erpnext/docs/assets/img/accounts/shareholder/shareledger_2.png b/erpnext/docs/assets/img/accounts/shareholder/shareledger_2.png
new file mode 100644
index 0000000..1d14ecc
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/shareledger_2.png
Binary files differ
diff --git a/erpnext/docs/assets/img/accounts/shareholder/sharetransfer_issue_tonystark.png b/erpnext/docs/assets/img/accounts/shareholder/sharetransfer_issue_tonystark.png
new file mode 100644
index 0000000..535353f
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/sharetransfer_issue_tonystark.png
Binary files differ
diff --git a/erpnext/docs/user/manual/en/accounts/share/share_balance.md b/erpnext/docs/user/manual/en/accounts/share/share_balance.md
new file mode 100644
index 0000000..2e66036
--- /dev/null
+++ b/erpnext/docs/user/manual/en/accounts/share/share_balance.md
@@ -0,0 +1,9 @@
+# Share Balance
+
+This is a report view which gives the list of all the shares held by a given Shareholder and its value
+
+> Accounts > Share Balance
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/sharebalance_1.png">
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/sharebalance_2.png">
diff --git a/erpnext/docs/user/manual/en/accounts/share/share_ledger.md b/erpnext/docs/user/manual/en/accounts/share/share_ledger.md
new file mode 100644
index 0000000..05b85b1
--- /dev/null
+++ b/erpnext/docs/user/manual/en/accounts/share/share_ledger.md
@@ -0,0 +1,9 @@
+# Share Ledger
+
+This is a report view which gives the list of all the transactions made by a given Shareholder
+
+> Accounts > Share Ledger
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/shareledger_1.png">
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/shareledger_2.png">
diff --git a/erpnext/docs/user/manual/en/accounts/share/share_transfer.md b/erpnext/docs/user/manual/en/accounts/share/share_transfer.md
new file mode 100644
index 0000000..fc1207b
--- /dev/null
+++ b/erpnext/docs/user/manual/en/accounts/share/share_transfer.md
@@ -0,0 +1,9 @@
+# Shareholder Transfer
+
+There may be times when you want to change the share structure of your company; either by adding a new shareholder or by changing the existing proportion of shares between shareholders. A share transfer is the process of transferring existing shares from one person to another; either by sale or gift.
+
+You can directly create your Shareholders via
+
+> Accounts > Share Transfer
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/sharetransfer_issue_tonystark.png">
diff --git a/erpnext/docs/user/manual/en/accounts/share/shareholder.md b/erpnext/docs/user/manual/en/accounts/share/shareholder.md
new file mode 100644
index 0000000..dfa065d
--- /dev/null
+++ b/erpnext/docs/user/manual/en/accounts/share/shareholder.md
@@ -0,0 +1,21 @@
+# Shareholder
+
+A shareholder is any person, company or other institution that owns at least one share of a company’s stock. Because shareholders are a company's owners, they reap the benefits of the company's successes in the form of increased stock valuation. If the company does poorly, however, shareholders can lose money if the price of its stock declines.
+
+A shareholder is uniquely identified by the Shareholder ID. Normally this ID is a naming series, starting with `SH-`. Also as soon as the Shareholder makes even a single transaction, a Folio number is allocated to him. This also is a unique to the Shareholder.
+
+You can directly create your Shareholders via
+
+> Accounts > Shareholder
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/shareholder_tonystark.png">
+
+A Shareholder can avail the features (operations) in the [Share Transfer](/docs/user/manual/en/accounts/share/share_transfer.html) process.
+
+> Note: Shareholders are separate from Contacts and Addresses. A Shareholder can
+have multiple Contacts and Addresses.
+
+### Contacts and Addresses
+
+[Contacts and Addresses](/docs/user/manual/en/CRM/contact.html) in ERPNext are stored separately so that you can
+attach multiple Contacts or Addresses to Shareholders, Customers and Suppliers
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index dce3c28..2aa4d5e 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -216,6 +216,7 @@
{'doctype': "Party Type", "party_type": "Supplier"},
{'doctype': "Party Type", "party_type": "Employee"},
{'doctype': "Party Type", "party_type": "Member"},
+ {'doctype': "Party Type", "party_type": "Shareholder"},
{'doctype': "Opportunity Type", "name": "Hub"},
{'doctype': "Opportunity Type", "name": _("Sales")},
@@ -246,6 +247,11 @@
{'doctype': 'Assessment Group', 'assessment_group_name': _('All Assessment Groups'),
'is_group': 1, 'parent_assessment_group': ''},
+ # Share Management
+ {"doctype": "Share Type", "title": _("Equity")},
+ {"doctype": "Share Type", "title": _("Preference")},
+
+
]
from erpnext.setup.setup_wizard.data.industry_type import get_industry_types