Merge pull request #22914 from newmatik/shipment_with_integration

feat: Shipment Doctype
diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py
index e278fd7..362f6cf 100644
--- a/erpnext/erpnext_integrations/utils.py
+++ b/erpnext/erpnext_integrations/utils.py
@@ -60,4 +60,12 @@
 				"default_account": payment_gateway_account
 			}]
 		})
-		mode_of_payment.insert(ignore_permissions=True)
\ No newline at end of file
+		mode_of_payment.insert(ignore_permissions=True)
+
+def get_tracking_url(carrier, tracking_number):
+	# Return the formatted Tracking URL.
+	tracking_url = ''
+	url_reference = frappe.get_value('Parcel Service', carrier, 'url_reference')
+	if url_reference:
+		tracking_url = frappe.render_template(url_reference, {'tracking_number': tracking_number})
+	return tracking_url
diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json
index 390fcd9..9068e33 100644
--- a/erpnext/stock/desk_page/stock/stock.json
+++ b/erpnext/stock/desk_page/stock/stock.json
@@ -8,7 +8,7 @@
   {
    "hidden": 0,
    "label": "Stock Transactions",
-   "links": "[\n     {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Material Request\",\n        \"name\": \"Material Request\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Stock Entry\",\n        \"name\": \"Stock Entry\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Customer\"\n        ],\n        \"label\": \"Delivery Note\",\n        \"name\": \"Delivery Note\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Supplier\"\n        ],\n        \"label\": \"Purchase Receipt\",\n        \"name\": \"Purchase Receipt\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Pick List\",\n        \"name\": \"Pick List\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Delivery Trip\",\n        \"name\": \"Delivery Trip\",\n        \"type\": \"doctype\"\n    }\n]"
+   "links": "[\n     {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Material Request\",\n        \"name\": \"Material Request\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Stock Entry\",\n        \"name\": \"Stock Entry\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Customer\"\n        ],\n        \"label\": \"Delivery Note\",\n        \"name\": \"Delivery Note\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Supplier\"\n        ],\n        \"label\": \"Purchase Receipt\",\n        \"name\": \"Purchase Receipt\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Pick List\",\n        \"name\": \"Pick List\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Shipment\",\n        \"name\": \"Shipment\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Delivery Trip\",\n        \"name\": \"Delivery Trip\",\n        \"type\": \"doctype\"\n    }\n]"
   },
   {
    "hidden": 0,
@@ -58,7 +58,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Stock",
- "modified": "2020-10-07 18:40:17.130207",
+ "modified": "2020-12-02 15:47:41.532942",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 251a26a..03921c5 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -156,6 +156,11 @@
 		}
 
 		if (!doc.is_return && doc.status!="Closed") {
+			if(doc.docstatus == 1) {
+				this.frm.add_custom_button(__('Shipment'), function() {
+					me.make_shipment() }, __('Create'));
+			}
+
 			if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1)
 				this.frm.add_custom_button(__('Installation Note'), function() {
 					me.make_installation_note() }, __('Create'));
@@ -220,6 +225,13 @@
 		}
 	},
 
+	make_shipment: function() {
+		frappe.model.open_mapped_doc({
+			method: "erpnext.stock.doctype.delivery_note.delivery_note.make_shipment",
+			frm: this.frm
+		})
+	},
+
 	make_sales_invoice: function() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index d04cf78..979e83d 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -569,6 +569,62 @@
 
 	return doclist
 
+@frappe.whitelist()
+def make_shipment(source_name, target_doc=None):
+	def postprocess(source, target):
+		user = frappe.db.get_value("User", frappe.session.user, ['email', 'full_name', 'phone', 'mobile_no'], as_dict=1)
+		target.pickup_contact_email = user.email
+		pickup_contact_display = '{}'.format(user.full_name)
+		if user:
+			if user.email:
+				pickup_contact_display += '<br>' + user.email
+			if user.phone:
+				pickup_contact_display += '<br>' + user.phone
+			if user.mobile_no and not user.phone:
+				pickup_contact_display += '<br>' + user.mobile_no
+		target.pickup_contact = pickup_contact_display
+
+		contact = frappe.db.get_value("Contact", source.contact_person, ['email_id', 'phone', 'mobile_no'], as_dict=1)
+		delivery_contact_display = '{}'.format(source.contact_display)
+		if contact:
+			if contact.email_id:
+				delivery_contact_display += '<br>' + contact.email_id
+			if contact.phone:
+				delivery_contact_display += '<br>' + contact.phone
+			if contact.mobile_no and not contact.phone:
+				delivery_contact_display += '<br>' + contact.mobile_no
+		target.delivery_contact = delivery_contact_display
+
+	doclist = get_mapped_doc("Delivery Note", source_name, 	{
+		"Delivery Note": {
+			"doctype": "Shipment",
+			"field_map": {
+				"grand_total": "value_of_goods",
+				"company": "pickup_company",
+				"company_address": "pickup_address_name",
+				"company_address_display": "pickup_address",
+				"address_display": "delivery_address",
+				"customer": "delivery_customer",
+				"shipping_address_name": "delivery_address_name",
+				"contact_person": "delivery_contact_name",
+				"contact_email": "delivery_contact_email"
+			},
+			"validation": {
+				"docstatus": ["=", 1]
+			}
+		},
+		"Delivery Note Item": {
+			"doctype": "Shipment Delivery Note",
+			"field_map": {
+				"name": "prevdoc_detail_docname",
+				"parent": "prevdoc_docname",
+				"parenttype": "prevdoc_doctype",
+				"base_amount": "grand_total"
+			}
+		}
+	}, target_doc, postprocess)
+	
+	return doclist
 
 @frappe.whitelist()
 def make_sales_return(source_name, target_doc=None):
diff --git a/erpnext/stock/doctype/shipment/__init__.py b/erpnext/stock/doctype/shipment/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/shipment/__init__.py
diff --git a/erpnext/stock/doctype/shipment/shipment.js b/erpnext/stock/doctype/shipment/shipment.js
new file mode 100644
index 0000000..5ccb7d2
--- /dev/null
+++ b/erpnext/stock/doctype/shipment/shipment.js
@@ -0,0 +1,447 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Shipment', {
+	address_query: function(frm, link_doctype, link_name, is_your_company_address) {
+		return {
+			query: 'frappe.contacts.doctype.address.address.address_query',
+			filters: {
+				link_doctype: link_doctype,
+				link_name: link_name,
+				is_your_company_address: is_your_company_address
+			}
+		};
+	},
+	contact_query: function(frm, link_doctype, link_name) {
+		return {
+			query: 'frappe.contacts.doctype.contact.contact.contact_query',
+			filters: {
+				link_doctype: link_doctype,
+				link_name: link_name
+			}
+		};
+	},
+	onload: function(frm) {
+		frm.set_query("delivery_address_name", () => {
+			let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}`;
+			return frm.events.address_query(frm, frm.doc.delivery_to_type, frm.doc[delivery_to], frm.doc.delivery_to_type === 'Company' ? 1 : 0);
+		});
+		frm.set_query("pickup_address_name", () => {
+			let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}`;
+			return frm.events.address_query(frm, frm.doc.pickup_from_type, frm.doc[pickup_from], frm.doc.pickup_from_type === 'Company' ? 1 : 0);
+		});
+		frm.set_query("delivery_contact_name", () => {
+			let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}`;
+			return frm.events.contact_query(frm, frm.doc.delivery_to_type, frm.doc[delivery_to]);
+		});
+		frm.set_query("pickup_contact_name", () => {
+			let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}`;
+			return frm.events.contact_query(frm, frm.doc.pickup_from_type, frm.doc[pickup_from]);
+		});
+		frm.set_query("delivery_note", "shipment_delivery_note", function() {
+			let customer = '';
+			if (frm.doc.delivery_to_type == "Customer") {
+				customer = frm.doc.delivery_customer;
+			}
+			if (frm.doc.delivery_to_type == "Company") {
+				customer = frm.doc.delivery_company;
+			}
+			if (customer) {
+				return {
+					filters: {
+						customer: customer,
+						docstatus: 1,
+						status: ["not in", ["Cancelled"]]
+					}
+				};
+			}
+		});
+	},
+	refresh: function() {
+		$('div[data-fieldname=pickup_address] > div > .clearfix').hide();
+		$('div[data-fieldname=pickup_contact] > div > .clearfix').hide();
+		$('div[data-fieldname=delivery_address] > div > .clearfix').hide();
+		$('div[data-fieldname=delivery_contact] > div > .clearfix').hide();
+	},
+	before_save: function(frm) {
+		let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}`;
+		frm.set_value("delivery_to", frm.doc[delivery_to]);
+		let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}`;
+		frm.set_value("pickup", frm.doc[pickup_from]);
+	},
+	set_pickup_company_address: function(frm) {
+		frappe.db.get_value('Address', {
+			address_title: frm.doc.pickup_company,
+			is_your_company_address: 1
+		}, 'name', (r) => {
+			frm.set_value("pickup_address_name", r.name);
+		});
+	},
+	set_delivery_company_address: function(frm) {
+		frappe.db.get_value('Address', {
+			address_title: frm.doc.delivery_company,
+			is_your_company_address: 1
+		}, 'name', (r) => {
+			frm.set_value("delivery_address_name", r.name);
+		});
+	},
+	pickup_from_type: function(frm) {
+		if (frm.doc.pickup_from_type == 'Company') {
+			frm.set_value("pickup_company", frappe.defaults.get_default('company'));
+			frm.set_value("pickup_customer", '');
+			frm.set_value("pickup_supplier", '');
+		} else {
+			frm.trigger('clear_pickup_fields');
+		}
+		if (frm.doc.pickup_from_type == 'Customer') {
+			frm.set_value("pickup_company", '');
+			frm.set_value("pickup_supplier", '');
+		}
+		if (frm.doc.pickup_from_type == 'Supplier') {
+			frm.set_value("pickup_customer", '');
+			frm.set_value("pickup_company", '');
+		}
+	},
+	delivery_to_type: function(frm) {
+		if (frm.doc.delivery_to_type == 'Company') {
+			frm.set_value("delivery_company", frappe.defaults.get_default('company'));
+			frm.set_value("delivery_customer", '');
+			frm.set_value("delivery_supplier", '');
+		} else {
+			frm.trigger('clear_delivery_fields');
+		}
+		if (frm.doc.delivery_to_type == 'Customer') {
+			frm.set_value("delivery_company", '');
+			frm.set_value("delivery_supplier", '');
+		}
+		if (frm.doc.delivery_to_type == 'Supplier') {
+			frm.set_value("delivery_customer", '');
+			frm.set_value("delivery_company", '');
+			frm.toggle_display("shipment_delivery_note", false);
+		} else {
+			frm.toggle_display("shipment_delivery_note", true);
+		}
+	},
+	delivery_address_name: function(frm) {
+		if (frm.doc.delivery_to_type == 'Company') {
+			erpnext.utils.get_address_display(frm, 'delivery_address_name', 'delivery_address', true);
+		} else {
+			erpnext.utils.get_address_display(frm, 'delivery_address_name', 'delivery_address', false);
+		}
+	},
+	pickup_address_name: function(frm) {
+		if (frm.doc.pickup_from_type == 'Company') {
+			erpnext.utils.get_address_display(frm, 'pickup_address_name', 'pickup_address', true);
+		} else {
+			erpnext.utils.get_address_display(frm, 'pickup_address_name', 'pickup_address', false);
+		}
+	},
+	get_contact_display: function(frm, contact_name, contact_type) {
+		frappe.call({
+			method: "frappe.contacts.doctype.contact.contact.get_contact_details",
+			args: { contact: contact_name },
+			callback: function(r) {
+				if (r.message) {
+					if (!(r.message.contact_email && (r.message.contact_phone || r.message.contact_mobile))) {
+						if (contact_type == 'Delivery') {
+							frm.set_value('delivery_contact_name', '');
+							frm.set_value('delivery_contact', '');
+						} else {
+							frm.set_value('pickup_contact_name', '');
+							frm.set_value('pickup_contact', '');
+						}
+						frappe.throw(__("Email or Phone/Mobile of the Contact are mandatory to continue.") + "</br>" + __("Please set Email/Phone for the contact") + ` <a href='#Form/Contact/${contact_name}'>${contact_name}</a>`);
+					}
+					let contact_display = r.message.contact_display;
+					if (r.message.contact_email) {
+						contact_display += '<br>' + r.message.contact_email;
+					}
+					if (r.message.contact_phone) {
+						contact_display += '<br>' + r.message.contact_phone;
+					}
+					if (r.message.contact_mobile && !r.message.contact_phone) {
+						contact_display += '<br>' + r.message.contact_mobile;
+					}
+					if (contact_type == 'Delivery') {
+						frm.set_value('delivery_contact', contact_display);
+						if (r.message.contact_email) {
+							frm.set_value('delivery_contact_email', r.message.contact_email);
+						}
+					} else {
+						frm.set_value('pickup_contact', contact_display);
+						if (r.message.contact_email) {
+							frm.set_value('pickup_contact_email', r.message.contact_email);
+						}
+					}
+				}
+			}
+		});
+	},
+	delivery_contact_name: function(frm) {
+		if (frm.doc.delivery_contact_name) {
+			frm.events.get_contact_display(frm, frm.doc.delivery_contact_name, 'Delivery');
+		}
+	},
+	pickup_contact_name: function(frm) {
+		if (frm.doc.pickup_contact_name) {
+			frm.events.get_contact_display(frm, frm.doc.pickup_contact_name, 'Pickup');
+		}
+	},
+	pickup_contact_person: function(frm) {
+		if (frm.doc.pickup_contact_person) {
+			frappe.call({
+				method: "erpnext.stock.doctype.shipment.shipment.get_company_contact",
+				args: { user: frm.doc.pickup_contact_person },
+				callback: function({ message }) {
+					const r = message;
+					let contact_display = `${r.first_name} ${r.last_name}`;
+					if (r.email) {
+						contact_display += `<br>${ r.email }`;
+						frm.set_value('pickup_contact_email', r.email);
+					}
+					if (r.phone) {
+						contact_display += `<br>${ r.phone }`;
+					}
+					if (r.mobile_no && !r.phone) {
+						contact_display += `<br>${ r.mobile_no }`;
+					}
+					frm.set_value('pickup_contact', contact_display);
+				}
+			});
+		} else {
+			if (frm.doc.pickup_from_type === 'Company') {
+				frappe.call({
+					method: "erpnext.stock.doctype.shipment.shipment.get_company_contact",
+					args: { user: frappe.session.user },
+					callback: function({ message }) {
+						const r = message;
+						let contact_display = `${r.first_name} ${r.last_name}`;
+						if (r.email) {
+							contact_display += `<br>${ r.email }`;
+							frm.set_value('pickup_contact_email', r.email);
+						}
+						if (r.phone) {
+							contact_display += `<br>${ r.phone }`;
+						}
+						if (r.mobile_no && !r.phone) {
+							contact_display += `<br>${ r.mobile_no }`;
+						}
+						frm.set_value('pickup_contact', contact_display);
+					}
+				});
+			}
+		}
+	},
+	set_company_contact: function(frm, delivery_type) {
+		frappe.db.get_value('User', { name: frappe.session.user }, ['full_name', 'last_name', 'email', 'phone', 'mobile_no'], (r) => {
+			if (!(r.last_name && r.email && (r.phone || r.mobile_no))) {
+				if (delivery_type == 'Delivery') {
+					frm.set_value('delivery_company', '');
+					frm.set_value('delivery_contact', '');
+				} else {
+					frm.set_value('pickup_company', '');
+					frm.set_value('pickup_contact', '');
+				}
+				frappe.throw(__("Last Name, Email or Phone/Mobile of the user are mandatory to continue.") + "</br>" + __("Please first set Last Name, Email and Phone for the user") + ` <a href="#Form/User/${frappe.session.user}">${frappe.session.user}</a>`);
+			}
+			let contact_display = r.full_name;
+			if (r.email) {
+				contact_display += '<br>' + r.email;
+			}
+			if (r.phone) {
+				contact_display += '<br>' + r.phone;
+			}
+			if (r.mobile_no && !r.phone) {
+				contact_display += '<br>' + r.mobile_no;
+			}
+			if (delivery_type == 'Delivery') {
+				frm.set_value('delivery_contact', contact_display);
+				if (r.email) {
+					frm.set_value('delivery_contact_email', r.email);
+				}
+			} else {
+				frm.set_value('pickup_contact', contact_display);
+				if (r.email) {
+					frm.set_value('pickup_contact_email', r.email);
+				}
+			}
+		});
+		frm.set_value('pickup_contact_person', frappe.session.user);
+	},
+	pickup_company: function(frm) {
+		if (frm.doc.pickup_from_type == 'Company'  && frm.doc.pickup_company) {
+			frm.trigger('set_pickup_company_address');
+			frm.events.set_company_contact(frm, 'Pickup');
+		}
+	},
+	delivery_company: function(frm) {
+		if (frm.doc.delivery_to_type == 'Company' && frm.doc.delivery_company) {
+			frm.trigger('set_delivery_company_address');
+			frm.events.set_company_contact(frm, 'Delivery');
+		}
+	},
+	delivery_customer: function(frm) {
+		frm.trigger('clear_delivery_fields');
+		if (frm.doc.delivery_customer) {
+			frm.events.set_address_name(frm, 'Customer', frm.doc.delivery_customer, 'Delivery');
+			frm.events.set_contact_name(frm, 'Customer', frm.doc.delivery_customer, 'Delivery');
+		}
+	},
+	delivery_supplier: function(frm) {
+		frm.trigger('clear_delivery_fields');
+		if (frm.doc.delivery_supplier) {
+			frm.events.set_address_name(frm, 'Supplier', frm.doc.delivery_supplier, 'Delivery');
+			frm.events.set_contact_name(frm, 'Supplier', frm.doc.delivery_supplier, 'Delivery');
+		}
+	},
+	pickup_customer: function(frm) {
+		if (frm.doc.pickup_customer) {
+			frm.events.set_address_name(frm, 'Customer', frm.doc.pickup_customer, 'Pickup');
+			frm.events.set_contact_name(frm, 'Customer', frm.doc.pickup_customer, 'Pickup');
+		}
+	},
+	pickup_supplier: function(frm) {
+		if (frm.doc.pickup_supplier) {
+			frm.events.set_address_name(frm, 'Supplier', frm.doc.pickup_supplier, 'Pickup');
+			frm.events.set_contact_name(frm, 'Supplier', frm.doc.pickup_supplier, 'Pickup');
+		}
+	},
+	set_address_name: function(frm, ref_doctype, ref_docname, delivery_type) {
+		frappe.call({
+			method: "erpnext.stock.doctype.shipment.shipment.get_address_name",
+			args: {
+				ref_doctype: ref_doctype,
+				docname: ref_docname
+			},
+			callback: function(r) {
+				if (r.message) {
+					if (delivery_type == 'Delivery') {
+						frm.set_value('delivery_address_name', r.message);
+					} else {
+						frm.set_value('pickup_address_name', r.message);
+					}
+				}
+			}
+		});
+	},
+	set_contact_name: function(frm, ref_doctype, ref_docname, delivery_type) {
+		frappe.call({
+			method: "erpnext.stock.doctype.shipment.shipment.get_contact_name",
+			args: {
+				ref_doctype: ref_doctype,
+				docname: ref_docname
+			},
+			callback: function(r) {
+				if (r.message) {
+					if (delivery_type == 'Delivery') {
+						frm.set_value('delivery_contact_name', r.message);
+					} else {
+						frm.set_value('pickup_contact_name', r.message);
+					}
+				}
+			}
+		});
+	},
+	add_template: function(frm) {
+		if (frm.doc.parcel_template) {
+			frappe.model.with_doc("Shipment Parcel Template", frm.doc.parcel_template, () => {
+				let parcel_template = frappe.model.get_doc("Shipment Parcel Template", frm.doc.parcel_template);
+				let row = frappe.model.add_child(frm.doc, "Shipment Parcel", "shipment_parcel");
+				row.length = parcel_template.length;
+				row.width = parcel_template.width;
+				row.height = parcel_template.height;
+				row.weight = parcel_template.weight;
+				frm.refresh_fields("shipment_parcel");
+			});
+		}
+	},
+	pickup_date: function(frm) {
+		if (frm.doc.pickup_date < frappe.datetime.get_today()) {
+			frappe.throw(__("Pickup Date cannot be before this day"));
+		}
+		if (frm.doc.pickup_date == frappe.datetime.get_today()) {
+			var pickup_time = frm.events.get_pickup_time(frm);
+			frm.set_value("pickup_from", pickup_time);
+			frm.trigger('set_pickup_to_time');
+		}
+	},
+	pickup_from: function(frm) {
+		var pickup_time = frm.events.get_pickup_time(frm);
+		if (frm.doc.pickup_from && frm.doc.pickup_date == frappe.datetime.get_today()) {
+			let current_hour = pickup_time.split(':')[0];
+			let current_min = pickup_time.split(':')[1];
+			let pickup_hour = frm.doc.pickup_from.split(':')[0];
+			let pickup_min = frm.doc.pickup_from.split(':')[1];
+			if (pickup_hour < current_hour || (pickup_hour == current_hour && pickup_min < current_min)) {
+				frm.set_value("pickup_from", pickup_time);
+				frappe.throw(__("Pickup Time cannot be in the past"));
+			}
+		}
+		frm.trigger('set_pickup_to_time');
+	},
+	get_pickup_time: function() {
+		let current_hour = new Date().getHours();
+		let current_min = new Date().toLocaleString('en-US', {minute: 'numeric'});
+		if (current_min < 30) {
+			current_min = '30';
+		} else {
+			current_min = '00';
+			current_hour = Number(current_hour)+1;
+		}
+		let pickup_time = current_hour +':'+ current_min;
+		return pickup_time;
+	},
+	set_pickup_to_time: function(frm) {
+		let pickup_to_hour = Number(frm.doc.pickup_from.split(':')[0])+5;
+		let pickup_to_min = frm.doc.pickup_from.split(':')[1];
+		let pickup_to = pickup_to_hour +':'+ pickup_to_min;
+		frm.set_value("pickup_to", pickup_to);
+	},
+	clear_pickup_fields: function(frm) {
+		let fields = ["pickup_address_name", "pickup_contact_name", "pickup_address", "pickup_contact", "pickup_contact_email", "pickup_contact_person"];
+		for (let field of fields) {
+			frm.set_value(field,  '');
+		}
+	},
+	clear_delivery_fields: function(frm) {
+		let fields = ["delivery_address_name", "delivery_contact_name", "delivery_address", "delivery_contact", "delivery_contact_email"];
+		for (let field of fields) {
+			frm.set_value(field,  '');
+		}
+	},
+	remove_email_row: function(frm, table, fieldname) {
+		$.each(frm.doc[table] || [], function(i, detail) {
+			if (detail.email === fieldname) {
+				cur_frm.get_field(table).grid.grid_rows[i].remove();
+			}
+		});
+	}
+});
+
+frappe.ui.form.on('Shipment Delivery Note', {
+	delivery_note: function(frm, cdt, cdn) {
+		let row = locals[cdt][cdn];
+		if (row.delivery_note) {
+			let row_index = row.idx - 1;
+			if (validate_duplicate(frm, 'shipment_delivery_note', row.delivery_note, row_index)) {
+				frappe.throw(__("You have entered a duplicate Delivery Note on Row") + ` ${row.idx}. ` + __("Please rectify and try again."));
+			}
+		}
+	},
+	grand_total: function(frm, cdt, cdn) {
+		let row = locals[cdt][cdn];
+		if (row.grand_total) {
+			var value_of_goods = parseFloat(frm.doc.value_of_goods)+parseFloat(row.grand_total);
+			frm.set_value("value_of_goods", Math.round(value_of_goods));
+			frm.refresh_fields("value_of_goods");
+		}
+	},
+});
+
+var validate_duplicate =  function(frm, table, fieldname, index) {
+	return (
+		table === 'shipment_delivery_note'
+			? frm.doc[table].some((detail, i) => detail.delivery_note === fieldname && !(index === i))
+			: frm.doc[table].some((detail, i) => detail.email === fieldname && !(index === i))
+	);
+};
diff --git a/erpnext/stock/doctype/shipment/shipment.json b/erpnext/stock/doctype/shipment/shipment.json
new file mode 100644
index 0000000..37a9cc6
--- /dev/null
+++ b/erpnext/stock/doctype/shipment/shipment.json
@@ -0,0 +1,471 @@
+{
+ "actions": [],
+ "autoname": "SHIPMENT-.#####",
+ "creation": "2020-07-09 10:58:52.508703",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "heading_pickup_from",
+  "pickup_from_type",
+  "pickup_company",
+  "pickup_customer",
+  "pickup_supplier",
+  "pickup",
+  "pickup_address_name",
+  "pickup_address",
+  "pickup_contact_person",
+  "pickup_contact_name",
+  "pickup_contact_email",
+  "pickup_contact",
+  "column_break_2",
+  "heading_delivery_to",
+  "delivery_to_type",
+  "delivery_company",
+  "delivery_customer",
+  "delivery_supplier",
+  "delivery_to",
+  "delivery_address_name",
+  "delivery_address",
+  "delivery_contact_name",
+  "delivery_contact_email",
+  "delivery_contact",
+  "parcels_section",
+  "shipment_parcel",
+  "parcel_template",
+  "add_template",
+  "column_break_28",
+  "shipment_delivery_note",
+  "shipment_details_section",
+  "pallets",
+  "value_of_goods",
+  "pickup_date",
+  "pickup_from",
+  "pickup_to",
+  "column_break_36",
+  "shipment_type",
+  "pickup_type",
+  "incoterm",
+  "description_of_content",
+  "section_break_40",
+  "shipment_information_section",
+  "service_provider",
+  "shipment_id",
+  "shipment_amount",
+  "status",
+  "tracking_url",
+  "column_break_55",
+  "carrier",
+  "carrier_service",
+  "awb_number",
+  "tracking_status",
+  "tracking_status_info",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "heading_pickup_from",
+   "fieldtype": "Heading",
+   "label": "Pickup from"
+  },
+  {
+   "default": "Company",
+   "fieldname": "pickup_from_type",
+   "fieldtype": "Select",
+   "label": "Pickup from",
+   "options": "Company\nCustomer\nSupplier"
+  },
+  {
+   "depends_on": "eval:doc.pickup_from_type == 'Company'",
+   "fieldname": "pickup_company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company"
+  },
+  {
+   "depends_on": "eval:doc.pickup_from_type == 'Customer'",
+   "fieldname": "pickup_customer",
+   "fieldtype": "Link",
+   "label": "Customer",
+   "options": "Customer"
+  },
+  {
+   "depends_on": "eval:doc.pickup_from_type == 'Supplier'",
+   "fieldname": "pickup_supplier",
+   "fieldtype": "Link",
+   "label": "Supplier",
+   "options": "Supplier"
+  },
+  {
+   "fieldname": "pickup",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "in_list_view": 1,
+   "label": "Pickup From",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.pickup_customer || doc.pickup_supplier || doc.pickup_from_type == \"Company\"",
+   "fieldname": "pickup_address_name",
+   "fieldtype": "Link",
+   "label": "Address",
+   "options": "Address",
+   "reqd": 1
+  },
+  {
+   "fieldname": "pickup_address",
+   "fieldtype": "Small Text",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.pickup_customer || doc.pickup_supplier || doc.pickup_from_type !== \"Company\"",
+   "fieldname": "pickup_contact_name",
+   "fieldtype": "Link",
+   "label": "Contact",
+   "mandatory_depends_on": "eval: doc.pickup_from_type !== 'Company'",
+   "options": "Contact"
+  },
+  {
+   "fieldname": "pickup_contact_email",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Contact Email",
+   "read_only": 1
+  },
+  {
+   "fieldname": "pickup_contact",
+   "fieldtype": "Small Text",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "heading_delivery_to",
+   "fieldtype": "Heading",
+   "label": "Delivery to"
+  },
+  {
+   "default": "Customer",
+   "fieldname": "delivery_to_type",
+   "fieldtype": "Select",
+   "label": "Delivery to",
+   "options": "Company\nCustomer\nSupplier"
+  },
+  {
+   "depends_on": "eval:doc.delivery_to_type == 'Company'",
+   "fieldname": "delivery_company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company"
+  },
+  {
+   "depends_on": "eval:doc.delivery_to_type == 'Customer'",
+   "fieldname": "delivery_customer",
+   "fieldtype": "Link",
+   "label": "Customer",
+   "options": "Customer"
+  },
+  {
+   "depends_on": "eval:doc.delivery_to_type == 'Supplier'",
+   "fieldname": "delivery_supplier",
+   "fieldtype": "Link",
+   "label": "Supplier",
+   "options": "Supplier"
+  },
+  {
+   "fieldname": "delivery_to",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "in_list_view": 1,
+   "label": "Delivery To",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.delivery_customer || doc.delivery_supplier || doc.delivery_to_type == \"Company\"",
+   "fieldname": "delivery_address_name",
+   "fieldtype": "Link",
+   "label": "Address",
+   "options": "Address",
+   "reqd": 1
+  },
+  {
+   "fieldname": "delivery_address",
+   "fieldtype": "Small Text",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.delivery_customer || doc.delivery_supplier || doc.delivery_to_type == \"Company\"",
+   "fieldname": "delivery_contact_name",
+   "fieldtype": "Link",
+   "label": "Contact",
+   "mandatory_depends_on": "eval: doc.delivery_from_type !== 'Company'",
+   "options": "Contact"
+  },
+  {
+   "fieldname": "delivery_contact_email",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Contact Email",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.delivery_contact_name",
+   "fieldname": "delivery_contact",
+   "fieldtype": "Small Text",
+   "read_only": 1
+  },
+  {
+   "fieldname": "parcels_section",
+   "fieldtype": "Section Break",
+   "label": "Parcels"
+  },
+  {
+   "fieldname": "shipment_parcel",
+   "fieldtype": "Table",
+   "label": "Shipment Parcel",
+   "options": "Shipment Parcel"
+  },
+  {
+   "fieldname": "parcel_template",
+   "fieldtype": "Link",
+   "label": "Parcel Template",
+   "options": "Shipment Parcel Template"
+  },
+  {
+   "depends_on": "eval:doc.docstatus !== 1\n",
+   "fieldname": "add_template",
+   "fieldtype": "Button",
+   "label": "Add Template"
+  },
+  {
+   "fieldname": "column_break_28",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "shipment_details_section",
+   "fieldtype": "Section Break",
+   "label": "Shipment details"
+  },
+  {
+   "default": "No",
+   "fieldname": "pallets",
+   "fieldtype": "Select",
+   "label": "Pallets",
+   "options": "No\nYes"
+  },
+  {
+   "fieldname": "value_of_goods",
+   "fieldtype": "Currency",
+   "label": "Value of Goods",
+   "precision": "2",
+   "reqd": 1
+  },
+  {
+   "allow_on_submit": 1,
+   "fieldname": "pickup_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Pickup Date",
+   "reqd": 1
+  },
+  {
+   "allow_on_submit": 1,
+   "default": "09:00",
+   "fieldname": "pickup_from",
+   "fieldtype": "Time",
+   "label": "Pickup from"
+  },
+  {
+   "allow_on_submit": 1,
+   "default": "17:00",
+   "fieldname": "pickup_to",
+   "fieldtype": "Time",
+   "label": "Pickup to"
+  },
+  {
+   "fieldname": "column_break_36",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "Goods",
+   "fieldname": "shipment_type",
+   "fieldtype": "Select",
+   "label": "Shipment Type",
+   "options": "Goods\nDocuments"
+  },
+  {
+   "default": "Pickup",
+   "fieldname": "pickup_type",
+   "fieldtype": "Select",
+   "label": "Pickup Type",
+   "options": "Pickup\nSelf delivery"
+  },
+  {
+   "fieldname": "description_of_content",
+   "fieldtype": "Small Text",
+   "label": "Description of Content",
+   "reqd": 1
+  },
+  {
+   "fieldname": "section_break_40",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "shipment_information_section",
+   "fieldtype": "Section Break",
+   "label": "Shipment Information"
+  },
+  {
+   "fieldname": "service_provider",
+   "fieldtype": "Data",
+   "label": "Service Provider",
+   "no_copy": 1,
+   "print_hide": 1
+  },
+  {
+   "fieldname": "shipment_id",
+   "fieldtype": "Data",
+   "label": "Shipment ID",
+   "no_copy": 1,
+   "print_hide": 1
+  },
+  {
+   "fieldname": "shipment_amount",
+   "fieldtype": "Currency",
+   "label": "Shipment Amount",
+   "no_copy": 1,
+   "precision": "2",
+   "print_hide": 1
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "label": "Status",
+   "no_copy": 1,
+   "options": "Draft\nSubmitted\nBooked\nCancelled\nCompleted",
+   "print_hide": 1
+  },
+  {
+   "fieldname": "tracking_url",
+   "fieldtype": "Small Text",
+   "hidden": 1,
+   "label": "Tracking URL",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "carrier",
+   "fieldtype": "Data",
+   "label": "Carrier",
+   "no_copy": 1,
+   "print_hide": 1
+  },
+  {
+   "fieldname": "carrier_service",
+   "fieldtype": "Data",
+   "label": "Carrier Service",
+   "no_copy": 1,
+   "print_hide": 1
+  },
+  {
+   "fieldname": "awb_number",
+   "fieldtype": "Data",
+   "label": "AWB Number",
+   "no_copy": 1,
+   "print_hide": 1
+  },
+  {
+   "fieldname": "tracking_status",
+   "fieldtype": "Select",
+   "label": "Tracking Status",
+   "no_copy": 1,
+   "options": "\nIn Progress\nDelivered\nReturned\nLost",
+   "print_hide": 1
+  },
+  {
+   "fieldname": "tracking_status_info",
+   "fieldtype": "Data",
+   "label": "Tracking Status Info",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "hidden": 1,
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Shipment",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_55",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "incoterm",
+   "fieldtype": "Select",
+   "label": "Incoterm",
+   "options": "EXW (Ex Works)\nFCA (Free Carrier)\nCPT (Carriage Paid To)\nCIP (Carriage and Insurance Paid to)\nDPU (Delivered At Place Unloaded)\nDAP (Delivered At Place)\nDDP (Delivered Duty Paid)"
+  },
+  {
+   "fieldname": "shipment_delivery_note",
+   "fieldtype": "Table",
+   "label": "Shipment Delivery Note",
+   "options": "Shipment Delivery Note"
+  },
+  {
+   "depends_on": "eval:doc.pickup_from_type === 'Company'",
+   "fieldname": "pickup_contact_person",
+   "fieldtype": "Link",
+   "label": "Pickup Contact Person",
+   "mandatory_depends_on": "eval:doc.pickup_from_type === 'Company'",
+   "options": "User"
+  }
+ ],
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2020-12-02 15:43:44.607039",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Shipment",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/shipment/shipment.py b/erpnext/stock/doctype/shipment/shipment.py
new file mode 100644
index 0000000..de0c243
--- /dev/null
+++ b/erpnext/stock/doctype/shipment/shipment.py
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import flt
+from frappe.model.document import Document
+from erpnext.accounts.party import get_party_shipping_address
+from frappe.contacts.doctype.contact.contact import get_default_contact
+
+class Shipment(Document):
+	def validate(self):
+		self.validate_weight()
+		self.set_value_of_goods()
+		if self.docstatus == 0:
+			self.status = 'Draft'
+
+	def on_submit(self):
+		if not self.shipment_parcel:
+			frappe.throw(_('Please enter Shipment Parcel information'))
+		if self.value_of_goods == 0:
+			frappe.throw(_('Value of goods cannot be 0'))
+		self.status = 'Submitted'
+
+	def on_cancel(self):
+		self.status = 'Cancelled'
+
+	def validate_weight(self):
+		for parcel in self.shipment_parcel:
+			if flt(parcel.weight) <= 0:
+				frappe.throw(_('Parcel weight cannot be 0'))
+
+	def set_value_of_goods(self):
+		value_of_goods = 0
+		for entry in self.get("shipment_delivery_note"):
+			value_of_goods += flt(entry.get("grand_total"))
+		self.value_of_goods = value_of_goods if value_of_goods else self.value_of_goods
+
+@frappe.whitelist()
+def get_address_name(ref_doctype, docname):
+	# Return address name
+	return get_party_shipping_address(ref_doctype, docname)
+
+@frappe.whitelist()
+def get_contact_name(ref_doctype, docname):
+	# Return address name
+	return get_default_contact(ref_doctype, docname)
+
+@frappe.whitelist()
+def get_company_contact(user):
+	contact = frappe.db.get_value('User', user, [
+		'first_name',
+		'last_name',
+		'email',
+		'phone',
+		'mobile_no',
+		'gender',
+	], as_dict=1)
+	if not contact.phone:
+		contact.phone = contact.mobile_no
+	return contact
diff --git a/erpnext/stock/doctype/shipment/shipment_list.js b/erpnext/stock/doctype/shipment/shipment_list.js
new file mode 100644
index 0000000..52b052c
--- /dev/null
+++ b/erpnext/stock/doctype/shipment/shipment_list.js
@@ -0,0 +1,8 @@
+frappe.listview_settings['Shipment'] = {
+	add_fields: ["status"],
+	get_indicator: function(doc) {
+		if (doc.status=='Booked') {
+			return [__("Booked"), "green"];
+		}
+	}
+};
\ No newline at end of file
diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py
new file mode 100644
index 0000000..e1fa207
--- /dev/null
+++ b/erpnext/stock/doctype/shipment/test_shipment.py
@@ -0,0 +1,240 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+from datetime import date, timedelta
+
+import frappe
+import unittest
+from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment
+
+class TestShipment(unittest.TestCase):
+	def test_shipment_from_delivery_note(self):
+		delivery_note = create_test_delivery_note()
+		delivery_note.submit()
+		shipment = create_test_shipment([ delivery_note ])
+		shipment.submit()
+		second_shipment = make_shipment(delivery_note.name)
+		self.assertEqual(second_shipment.value_of_goods, delivery_note.grand_total)
+		self.assertEqual(len(second_shipment.shipment_delivery_note), 1)
+		self.assertEqual(second_shipment.shipment_delivery_note[0].delivery_note, delivery_note.name)
+
+def create_test_delivery_note():
+	company = get_shipment_company()
+	customer = get_shipment_customer()
+	item = get_shipment_item(company.name)
+	posting_date = date.today() + timedelta(days=1)
+	
+	create_material_receipt(item, company.name)
+	delivery_note = frappe.new_doc("Delivery Note")
+	delivery_note.company = company.name
+	delivery_note.posting_date = posting_date.strftime("%Y-%m-%d")
+	delivery_note.posting_time = '10:00'
+	delivery_note.customer = customer.name
+	delivery_note.append('items',
+		{
+			"item_code": item.name,
+			"item_name": item.item_name,
+			"description": 'Test delivery note for shipment',
+			"qty": 5,
+			"uom": 'Nos',
+			"warehouse": 'Stores - SC',
+			"rate": item.standard_rate,
+			"cost_center": 'Main - SC'
+		}
+	)
+	delivery_note.insert()
+	frappe.db.commit()
+	return delivery_note
+
+
+def create_test_shipment(delivery_notes = None):
+	company = get_shipment_company()
+	company_address = get_shipment_company_address(company.name)
+	customer = get_shipment_customer()
+	customer_address = get_shipment_customer_address(customer.name)
+	customer_contact = get_shipment_customer_contact(customer.name)
+	posting_date = date.today() + timedelta(days=5)
+
+	shipment = frappe.new_doc("Shipment")
+	shipment.pickup_from_type = 'Company'
+	shipment.pickup_company = company.name
+	shipment.pickup_address_name = company_address.name
+	shipment.delivery_to_type = 'Customer'
+	shipment.delivery_customer = customer.name
+	shipment.delivery_address_name = customer_address.name
+	shipment.delivery_contact_name = customer_contact.name
+	shipment.pallets = 'No'
+	shipment.shipment_type = 'Goods'
+	shipment.value_of_goods = 1000
+	shipment.pickup_type = 'Pickup'
+	shipment.pickup_date = posting_date.strftime("%Y-%m-%d")
+	shipment.pickup_from = '09:00'
+	shipment.pickup_to = '17:00'
+	shipment.description_of_content = 'unit test entry'
+	for delivery_note in delivery_notes:
+		shipment.append('shipment_delivery_note', 
+			{
+				"delivery_note": delivery_note.name
+			}
+		)
+	shipment.append('shipment_parcel',
+		{
+			"length": 5,
+			"width": 5,
+			"height": 5,
+			"weight": 5,
+			"count": 5
+		}
+	)
+	shipment.insert()
+	frappe.db.commit()
+	return shipment
+
+
+def get_shipment_customer_contact(customer_name):
+	contact_fname = 'Customer Shipment'
+	contact_lname = 'Testing'
+	customer_name = contact_fname + ' ' + contact_lname
+	contacts = frappe.get_all("Contact", fields=["name"], filters = {"name": customer_name})
+	if len(contacts):
+		return contacts[0]
+	else:
+		return create_customer_contact(contact_fname, contact_lname)
+
+
+def get_shipment_customer_address(customer_name):
+	address_title = customer_name + ' address 123'
+	customer_address = frappe.get_all("Address", fields=["name"], filters = {"address_title": address_title})
+	if len(customer_address):
+		return customer_address[0]
+	else:
+		return create_shipment_address(address_title, customer_name, 81929)
+
+def get_shipment_customer():
+	customer_name = 'Shipment Customer'
+	customer = frappe.get_all("Customer", fields=["name"], filters = {"name": customer_name})
+	if len(customer):
+		return customer[0]
+	else:
+		return create_shipment_customer(customer_name)
+
+def get_shipment_company_address(company_name):
+	address_title = company_name + ' address 123'
+	addresses = frappe.get_all("Address", fields=["name"], filters = {"address_title": address_title})
+	if len(addresses):
+		return addresses[0]
+	else:
+		return create_shipment_address(address_title, company_name, 80331)
+
+def get_shipment_company():
+	company_name = 'Shipment Company'
+	abbr = 'SC'
+	companies = frappe.get_all("Company", fields=["name"], filters = {"company_name": company_name})
+	if len(companies):
+		return companies[0]
+	else:
+		return create_shipment_company(company_name, abbr)
+
+def get_shipment_item(company_name):
+	item_name = 'Testing Shipment item'
+	items = frappe.get_all("Item",
+		fields=["name", "item_name", "item_code", "standard_rate"],
+		filters = {"item_name": item_name}
+	)
+	if len(items):
+		return items[0]
+	else:
+		return create_shipment_item(item_name, company_name)
+
+def create_shipment_address(address_title, company_name, postal_code):
+	address = frappe.new_doc("Address")
+	address.address_title = address_title
+	address.address_type = 'Shipping'
+	address.address_line1 = company_name + ' address line 1'
+	address.city = 'Random City'
+	address.postal_code = postal_code
+	address.country = 'Germany'
+	address.insert()
+	return address
+
+
+def create_customer_contact(fname, lname):
+	customer = frappe.new_doc("Contact")
+	customer.customer_name = fname + ' ' + lname
+	customer.first_name = fname
+	customer.last_name = lname
+	customer.is_primary_contact = 1
+	customer.is_billing_contact = 1
+	customer.append('email_ids',
+		{
+			'email_id': 'randomme@email.com',
+			'is_primary': 1
+		}
+	)
+	customer.append('phone_nos',
+		{
+			'phone': '123123123',
+			'is_primary_phone': 1,
+			'is_primary_mobile_no': 1
+		}
+	)
+	customer.status = 'Passive'
+	customer.insert()
+	return customer
+
+
+def create_shipment_company(company_name, abbr):
+	company = frappe.new_doc("Company")
+	company.company_name = company_name
+	company.abbr = abbr
+	company.default_currency = 'EUR'
+	company.country = 'Germany'
+	company.insert()
+	return company
+
+def create_shipment_customer(customer_name):
+	customer = frappe.new_doc("Customer")
+	customer.customer_name = customer_name
+	customer.customer_type = 'Company'
+	customer.customer_group = 'All Customer Groups'
+	customer.territory = 'All Territories'
+	customer.gst_category = 'Unregistered'
+	customer.insert()
+	return customer
+
+def create_material_receipt(item, company):
+	posting_date = date.today()
+	stock = frappe.new_doc("Stock Entry")
+	stock.company = company
+	stock.stock_entry_type = 'Material Receipt'
+	stock.posting_date = posting_date.strftime("%Y-%m-%d")
+	stock.append('items',
+		{
+			"t_warehouse": 'Stores - SC',
+			"item_code": item.name,
+			"qty": 5,
+			"uom": 'Nos',
+			"basic_rate": item.standard_rate,
+			"cost_center": 'Main - SC'
+		}
+	)
+	stock.insert()
+	stock.submit()
+	
+
+def create_shipment_item(item_name, company_name):
+	item = frappe.new_doc("Item")
+	item.item_name = item_name
+	item.item_code = item_name
+	item.item_group = 'All Item Groups'
+	item.stock_uom = 'Nos'
+	item.standard_rate = 50
+	item.append('item_defaults',
+		{
+			"company": company_name,
+			"default_warehouse": 'Stores - SC'
+		}
+	)
+	item.insert()
+	return item
diff --git a/erpnext/stock/doctype/shipment_delivery_note/__init__.py b/erpnext/stock/doctype/shipment_delivery_note/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/shipment_delivery_note/__init__.py
diff --git a/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.json b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.json
new file mode 100644
index 0000000..8625913
--- /dev/null
+++ b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.json
@@ -0,0 +1,40 @@
+{
+ "actions": [],
+ "creation": "2020-07-09 11:52:57.939021",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "delivery_note",
+  "grand_total"
+ ],
+ "fields": [
+  {
+   "fieldname": "delivery_note",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Delivery Note",
+   "options": "Delivery Note",
+   "reqd": 1
+  },
+  {
+   "fieldname": "grand_total",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Value",
+   "read_only": 1
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-12-02 15:44:34.028703",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Shipment Delivery Note",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py
new file mode 100644
index 0000000..4342151
--- /dev/null
+++ b/erpnext/stock/doctype/shipment_delivery_note/shipment_delivery_note.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, 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 ShipmentDeliveryNote(Document):
+	pass
diff --git a/erpnext/stock/doctype/shipment_parcel/__init__.py b/erpnext/stock/doctype/shipment_parcel/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/shipment_parcel/__init__.py
diff --git a/erpnext/stock/doctype/shipment_parcel/shipment_parcel.json b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.json
new file mode 100644
index 0000000..6943edc
--- /dev/null
+++ b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.json
@@ -0,0 +1,65 @@
+{
+ "actions": [],
+ "creation": "2020-07-09 11:28:48.887737",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "length",
+  "width",
+  "height",
+  "weight",
+  "count"
+ ],
+ "fields": [
+  {
+   "fieldname": "length",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Length (cm)",
+   "reqd": 1
+  },
+  {
+   "fieldname": "width",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Width (cm)",
+   "reqd": 1
+  },
+  {
+   "fieldname": "height",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Height (cm)",
+   "reqd": 1
+  },
+  {
+   "fieldname": "weight",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Weight (kg)",
+   "precision": "1",
+   "reqd": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "count",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Count",
+   "reqd": 1
+  }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-07-09 12:54:14.847170",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Shipment Parcel",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/shipment_parcel/shipment_parcel.py b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.py
new file mode 100644
index 0000000..53e6ed5
--- /dev/null
+++ b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, 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 ShipmentParcel(Document):
+	pass
diff --git a/erpnext/stock/doctype/shipment_parcel_template/__init__.py b/erpnext/stock/doctype/shipment_parcel_template/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/shipment_parcel_template/__init__.py
diff --git a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.js b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.js
new file mode 100644
index 0000000..785a3b3
--- /dev/null
+++ b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Shipment Parcel Template', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json
new file mode 100644
index 0000000..4735d9f
--- /dev/null
+++ b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json
@@ -0,0 +1,78 @@
+{
+ "actions": [],
+ "autoname": "field:parcel_template_name",
+ "creation": "2020-07-09 11:43:43.470339",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "parcel_template_name",
+  "length",
+  "width",
+  "height",
+  "weight"
+ ],
+ "fields": [
+  {
+   "fieldname": "length",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Length (cm)",
+   "reqd": 1
+  },
+  {
+   "fieldname": "width",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Width (cm)",
+   "reqd": 1
+  },
+  {
+   "fieldname": "height",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Height (cm)",
+   "reqd": 1
+  },
+  {
+   "fieldname": "weight",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Weight (kg)",
+   "precision": "1",
+   "reqd": 1
+  },
+  {
+   "fieldname": "parcel_template_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Parcel Template Name",
+   "reqd": 1,
+   "unique": 1
+  }
+ ],
+ "links": [],
+ "modified": "2020-09-28 12:51:00.320421",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Shipment Parcel Template",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py
new file mode 100644
index 0000000..2a8d58d
--- /dev/null
+++ b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, 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 ShipmentParcelTemplate(Document):
+	pass
diff --git a/erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.py b/erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.py
new file mode 100644
index 0000000..6e2caa7
--- /dev/null
+++ b/erpnext/stock/doctype/shipment_parcel_template/test_shipment_parcel_template.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestShipmentParcelTemplate(unittest.TestCase):
+	pass