stockor 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +220 -0
- data/Guardfile +13 -0
- data/README.md +7 -0
- data/Rakefile +7 -0
- data/client/skr/Extension.coffee +12 -0
- data/client/skr/components/.gitkeep +0 -0
- data/client/skr/components/address/Address.coffee +21 -0
- data/client/skr/components/address/address.html +20 -0
- data/client/skr/index.js +21 -0
- data/client/skr/models/Address.coffee +17 -0
- data/client/skr/models/Base.coffee +8 -0
- data/client/skr/models/Customer.coffee +26 -0
- data/client/skr/models/GlAccount.coffee +10 -0
- data/client/skr/models/GlManualEntry.coffee +11 -0
- data/client/skr/models/GlPeriod.coffee +10 -0
- data/client/skr/models/GlPosting.coffee +15 -0
- data/client/skr/models/GlTransaction.coffee +16 -0
- data/client/skr/models/IaLine.coffee +19 -0
- data/client/skr/models/IaReason.coffee +12 -0
- data/client/skr/models/InvLine.coffee +27 -0
- data/client/skr/models/InventoryAdjustment.coffee +17 -0
- data/client/skr/models/Invoice.coffee +31 -0
- data/client/skr/models/Location.coffee +15 -0
- data/client/skr/models/PaymentTerm.coffee +11 -0
- data/client/skr/models/PickTicket.coffee +19 -0
- data/client/skr/models/PoLine.coffee +27 -0
- data/client/skr/models/PoReceipt.coffee +20 -0
- data/client/skr/models/PorLine.coffee +26 -0
- data/client/skr/models/PtLine.coffee +27 -0
- data/client/skr/models/PurchaseOrder.coffee +23 -0
- data/client/skr/models/SalesOrder.coffee +32 -0
- data/client/skr/models/Sku.coffee +21 -0
- data/client/skr/models/SkuLoc.coffee +21 -0
- data/client/skr/models/SkuTran.coffee +23 -0
- data/client/skr/models/SkuVendor.coffee +19 -0
- data/client/skr/models/SoLine.coffee +27 -0
- data/client/skr/models/Uom.coffee +17 -0
- data/client/skr/models/Vendor.coffee +28 -0
- data/client/skr/models/VoLine.coffee +23 -0
- data/client/skr/models/Voucher.coffee +22 -0
- data/client/skr/models/mixins/CodeField.coffee +5 -0
- data/client/skr/screens/.gitkeep +0 -0
- data/client/skr/screens/Base.coffee +3 -0
- data/client/skr/screens/base/index.js +5 -0
- data/client/skr/screens/base/index.scss +9 -0
- data/client/skr/screens/base/layout.html +3 -0
- data/client/skr/screens/customer-maint/CustomerMaint.coffee +49 -0
- data/client/skr/screens/customer-maint/index.js +5 -0
- data/client/skr/screens/customer-maint/index.scss +9 -0
- data/client/skr/screens/customer-maint/layout.html +32 -0
- data/client/skr/styles.scss +1 -0
- data/client/skr/views/.gitkeep +0 -0
- data/client/skr/views/Base.coffee +5 -0
- data/config/database.yml +9 -0
- data/config/lanes.rb +7 -0
- data/config/routes.rb +39 -0
- data/config/screens.rb +17 -0
- data/config.ru +5 -0
- data/db/.gitkeep +0 -0
- data/db/migrate/20120110142845_create_skr_sequential_ids.rb +35 -0
- data/db/migrate/20140202185309_create_skr_gl_accounts.rb +15 -0
- data/db/migrate/20140202193316_create_skr_gl_periods.rb +16 -0
- data/db/migrate/20140202193318_create_skr_gl_transactions.rb +14 -0
- data/db/migrate/20140202193319_create_skr_gl_postings.rb +16 -0
- data/db/migrate/20140202193700_create_skr_gl_manual_entries.rb +13 -0
- data/db/migrate/20140213040608_create_skr_payment_terms.rb +16 -0
- data/db/migrate/20140220031700_create_skr_addresses.rb +19 -0
- data/db/migrate/20140220031800_create_skr_locations.rb +19 -0
- data/db/migrate/20140220190836_create_skr_vendors.rb +22 -0
- data/db/migrate/20140220203029_create_skr_customers.rb +22 -0
- data/db/migrate/20140224034759_create_skr_skus.rb +22 -0
- data/db/migrate/20140225032853_create_skr_sku_locs.rb +21 -0
- data/db/migrate/20140320030501_create_skr_uoms.rb +19 -0
- data/db/migrate/20140321031604_create_skr_sku_vendors.rb +18 -0
- data/db/migrate/20140322012143_create_skr_ia_reasons.rb +14 -0
- data/db/migrate/20140322014401_create_skr_inventory_adjustments.rb +16 -0
- data/db/migrate/20140322023453_create_skr_ia_lines.rb +18 -0
- data/db/migrate/20140322035024_create_skr_sku_trans.rb +21 -0
- data/db/migrate/20140322223912_create_skr_sales_orders.rb +27 -0
- data/db/migrate/20140322223920_create_skr_so_lines.rb +25 -0
- data/db/migrate/20140323001446_create_so_details_view.rb +81 -0
- data/db/migrate/20140327202102_create_skr_purchase_orders.rb +20 -0
- data/db/migrate/20140327202107_create_skr_po_lines.rb +25 -0
- data/db/migrate/20140327202207_create_skr_pick_tickets.rb +16 -0
- data/db/migrate/20140327202209_create_skr_pt_lines.rb +23 -0
- data/db/migrate/20140327224000_create_skr_invoices.rb +25 -0
- data/db/migrate/20140327224002_create_skr_inv_lines.rb +23 -0
- data/db/migrate/20140330232808_create_skr_sku_loc_details_view.rb +31 -0
- data/db/migrate/20140330232810_create_skr_sku_qty_details_view.rb +48 -0
- data/db/migrate/20140400164729_create_skr_vouchers.rb +22 -0
- data/db/migrate/20140400164733_create_skr_vo_lines.rb +21 -0
- data/db/migrate/20140401164729_create_skr_po_receipt.rb +16 -0
- data/db/migrate/20140401164740_create_skr_por_line.rb +21 -0
- data/db/migrate/20140422024010_create_skr_inv_details_view.rb +42 -0
- data/db/schema.sql +2662 -0
- data/db/seed/chart_of_accounts.yml +168 -0
- data/db/seed/payment_terms.yml +60 -0
- data/db/seed.rb +29 -0
- data/lib/skr/access_roles.rb +28 -0
- data/lib/skr/concerns/acts_as_uom.rb +47 -0
- data/lib/skr/concerns/code_identifier.rb +43 -0
- data/lib/skr/concerns/gl_tran_extensions.rb +18 -0
- data/lib/skr/concerns/has_gl_transaction.rb +67 -0
- data/lib/skr/concerns/has_sku_loc_lines.rb +47 -0
- data/lib/skr/concerns/immutable_model.rb +32 -0
- data/lib/skr/concerns/inv_extensions.rb +24 -0
- data/lib/skr/concerns/is_order_like.rb +47 -0
- data/lib/skr/concerns/is_sku_loc_line.rb +65 -0
- data/lib/skr/concerns/locked_fields.rb +84 -0
- data/lib/skr/concerns/pt_extensions.rb +22 -0
- data/lib/skr/concerns/random_hash_code.rb +40 -0
- data/lib/skr/concerns/so_extensions.rb +30 -0
- data/lib/skr/concerns/state_machine.rb +61 -0
- data/lib/skr/concerns/visible_id_identifier.rb +53 -0
- data/lib/skr/configuration.rb +68 -0
- data/lib/skr/db/migration_helpers.rb +178 -0
- data/lib/skr/extension.rb +23 -0
- data/lib/skr/model.rb +19 -0
- data/lib/skr/models/address.rb +97 -0
- data/lib/skr/models/business_entity.rb +29 -0
- data/lib/skr/models/customer.rb +35 -0
- data/lib/skr/models/gl_account.rb +56 -0
- data/lib/skr/models/gl_manual_entry.rb +31 -0
- data/lib/skr/models/gl_period.rb +13 -0
- data/lib/skr/models/gl_posting.rb +54 -0
- data/lib/skr/models/gl_transaction.rb +175 -0
- data/lib/skr/models/ia_line.rb +129 -0
- data/lib/skr/models/ia_reason.rb +16 -0
- data/lib/skr/models/inv_line.rb +90 -0
- data/lib/skr/models/inventory_adjustment.rb +60 -0
- data/lib/skr/models/invoice.rb +159 -0
- data/lib/skr/models/location.rb +31 -0
- data/lib/skr/models/payment_term.rb +30 -0
- data/lib/skr/models/pick_ticket.rb +71 -0
- data/lib/skr/models/po_line.rb +69 -0
- data/lib/skr/models/po_receipt.rb +51 -0
- data/lib/skr/models/por_line.rb +80 -0
- data/lib/skr/models/pt_line.rb +74 -0
- data/lib/skr/models/purchase_order.rb +112 -0
- data/lib/skr/models/sales_order.rb +159 -0
- data/lib/skr/models/sequential_id.rb +23 -0
- data/lib/skr/models/sku.rb +99 -0
- data/lib/skr/models/sku_loc.rb +94 -0
- data/lib/skr/models/sku_tran.rb +111 -0
- data/lib/skr/models/sku_vendor.rb +26 -0
- data/lib/skr/models/so_line.rb +159 -0
- data/lib/skr/models/uom.rb +63 -0
- data/lib/skr/models/user_proxy.rb +60 -0
- data/lib/skr/models/vendor.rb +33 -0
- data/lib/skr/models/vo_line.rb +35 -0
- data/lib/skr/models/voucher.rb +119 -0
- data/lib/skr/standard_pricing_provider.rb +14 -0
- data/lib/skr/version.rb +3 -0
- data/lib/skr.rb +18 -0
- data/lib/stockor.rb +4 -0
- data/lib/tasks/debug-activity.rake +58 -0
- data/log/test.log +0 -0
- data/spec/fixtures/skr/address.yml +2 -0
- data/spec/fixtures/skr/customer.yml +2 -0
- data/spec/fixtures/skr/gl_account.yml +2 -0
- data/spec/fixtures/skr/gl_manual_entry.yml +2 -0
- data/spec/fixtures/skr/gl_period.yml +2 -0
- data/spec/fixtures/skr/gl_posting.yml +2 -0
- data/spec/fixtures/skr/gl_transaction.yml +2 -0
- data/spec/fixtures/skr/ia_line.yml +2 -0
- data/spec/fixtures/skr/ia_reason.yml +2 -0
- data/spec/fixtures/skr/inv_line.yml +2 -0
- data/spec/fixtures/skr/inventory_adjustment.yml +2 -0
- data/spec/fixtures/skr/invoice.yml +2 -0
- data/spec/fixtures/skr/location.yml +2 -0
- data/spec/fixtures/skr/payment_term.yml +2 -0
- data/spec/fixtures/skr/pick_ticket.yml +2 -0
- data/spec/fixtures/skr/po_line.yml +2 -0
- data/spec/fixtures/skr/po_receipt.yml +2 -0
- data/spec/fixtures/skr/por_line.yml +2 -0
- data/spec/fixtures/skr/pt_line.yml +2 -0
- data/spec/fixtures/skr/purchase_order.yml +2 -0
- data/spec/fixtures/skr/sales_order.yml +2 -0
- data/spec/fixtures/skr/sku.yml +2 -0
- data/spec/fixtures/skr/sku_loc.yml +2 -0
- data/spec/fixtures/skr/sku_tran.yml +2 -0
- data/spec/fixtures/skr/sku_vendor.yml +2 -0
- data/spec/fixtures/skr/so_line.yml +2 -0
- data/spec/fixtures/skr/uom.yml +2 -0
- data/spec/fixtures/skr/vendor.yml +2 -0
- data/spec/fixtures/skr/vo_line.yml +2 -0
- data/spec/fixtures/skr/voucher.yml +2 -0
- data/spec/skr/address.rb +10 -0
- data/spec/skr/concerns/code_identifier_spec.rb +45 -0
- data/spec/skr/customer.rb +10 -0
- data/spec/skr/gl_account.rb +10 -0
- data/spec/skr/gl_manual_entry.rb +10 -0
- data/spec/skr/gl_period.rb +10 -0
- data/spec/skr/gl_posting.rb +10 -0
- data/spec/skr/gl_transaction.rb +10 -0
- data/spec/skr/ia_line.rb +10 -0
- data/spec/skr/ia_reason.rb +10 -0
- data/spec/skr/inv_line.rb +10 -0
- data/spec/skr/inventory_adjustment.rb +10 -0
- data/spec/skr/invoice.rb +10 -0
- data/spec/skr/location.rb +10 -0
- data/spec/skr/models/AddressSpec.coffee +5 -0
- data/spec/skr/models/CustomerSpec.coffee +5 -0
- data/spec/skr/models/GlAccountSpec.coffee +5 -0
- data/spec/skr/models/GlManualEntrySpec.coffee +5 -0
- data/spec/skr/models/GlPeriodSpec.coffee +5 -0
- data/spec/skr/models/GlPostingSpec.coffee +5 -0
- data/spec/skr/models/GlTransactionSpec.coffee +5 -0
- data/spec/skr/models/IaLineSpec.coffee +5 -0
- data/spec/skr/models/IaReasonSpec.coffee +5 -0
- data/spec/skr/models/InvLineSpec.coffee +5 -0
- data/spec/skr/models/InventoryAdjustmentSpec.coffee +5 -0
- data/spec/skr/models/InvoiceSpec.coffee +5 -0
- data/spec/skr/models/LocationSpec.coffee +5 -0
- data/spec/skr/models/PaymentTermSpec.coffee +5 -0
- data/spec/skr/models/PickTicketSpec.coffee +5 -0
- data/spec/skr/models/PoLineSpec.coffee +5 -0
- data/spec/skr/models/PoReceiptSpec.coffee +5 -0
- data/spec/skr/models/PorLineSpec.coffee +5 -0
- data/spec/skr/models/PtLineSpec.coffee +5 -0
- data/spec/skr/models/PurchaseOrderSpec.coffee +5 -0
- data/spec/skr/models/SalesOrderSpec.coffee +5 -0
- data/spec/skr/models/SkuLocSpec.coffee +5 -0
- data/spec/skr/models/SkuSpec.coffee +5 -0
- data/spec/skr/models/SkuTranSpec.coffee +5 -0
- data/spec/skr/models/SkuVendorSpec.coffee +5 -0
- data/spec/skr/models/SoLineSpec.coffee +5 -0
- data/spec/skr/models/UomSpec.coffee +5 -0
- data/spec/skr/models/VendorSpec.coffee +5 -0
- data/spec/skr/models/VoLineSpec.coffee +5 -0
- data/spec/skr/models/VoucherSpec.coffee +5 -0
- data/spec/skr/payment_term.rb +10 -0
- data/spec/skr/pick_ticket.rb +10 -0
- data/spec/skr/po_line.rb +10 -0
- data/spec/skr/po_receipt.rb +10 -0
- data/spec/skr/por_line.rb +10 -0
- data/spec/skr/pt_line.rb +10 -0
- data/spec/skr/purchase_order.rb +10 -0
- data/spec/skr/sales_order.rb +10 -0
- data/spec/skr/screens/Base.coffee +7 -0
- data/spec/skr/screens/CustomerMaint.coffee +7 -0
- data/spec/skr/screens/vendor-maint/VendorMaintSpec.coffee +5 -0
- data/spec/skr/sku.rb +10 -0
- data/spec/skr/sku_loc.rb +10 -0
- data/spec/skr/sku_tran.rb +10 -0
- data/spec/skr/sku_vendor.rb +10 -0
- data/spec/skr/so_line.rb +10 -0
- data/spec/skr/spec_helper.rb +26 -0
- data/spec/skr/uom.rb +10 -0
- data/spec/skr/vendor.rb +10 -0
- data/spec/skr/views/AddressSpec.coffee +5 -0
- data/spec/skr/vo_line.rb +10 -0
- data/spec/skr/voucher.rb +10 -0
- data/stockor.gemspec +38 -0
- data/tmp/.gitkeep +0 -0
- metadata +414 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
|
|
3
|
+
# Invoices constitute a demand for payment for goods that have been delivered to a {Customer}.
|
|
4
|
+
# A invoice contains:
|
|
5
|
+
#
|
|
6
|
+
# * Customer contact information
|
|
7
|
+
# * An inventory location that goods will be taken from.
|
|
8
|
+
# * The customer provided {PurchaseOrder} Number
|
|
9
|
+
# * The Payment Terms that were extended. This will control how much time the Customer has to pay the invoice in full.
|
|
10
|
+
# * One or more SKUs, the quantity desired for each and the selling price for them.
|
|
11
|
+
#
|
|
12
|
+
# While an {Invoice} often originates with a {SalesOrder}, it does not have to.
|
|
13
|
+
# Sales that take place in a retail environment where the customer selects
|
|
14
|
+
# the goods and pays for them immediately do not require a sales order record.
|
|
15
|
+
#
|
|
16
|
+
# Once an invoice is saved, it immediately removes the SKUs from the {SkuLoc}
|
|
17
|
+
# and generates corresponding General Ledger entries debiting the asset account
|
|
18
|
+
# and crediting the customers receivables account.
|
|
19
|
+
#
|
|
20
|
+
# When payment is received against the Invoice,
|
|
21
|
+
# the receivables account is debited and the payments holding account is credited.
|
|
22
|
+
#
|
|
23
|
+
# invoice = Invoice.new( customer: Customer.find_by_code("ACME")
|
|
24
|
+
# invoice.lines.build({ sku: Sku.find_by_code('LABOR'), qty: 1, price: 8.27 })
|
|
25
|
+
# invoice.save
|
|
26
|
+
|
|
27
|
+
class Invoice < Skr::Model
|
|
28
|
+
|
|
29
|
+
has_visible_id
|
|
30
|
+
has_random_hash_code
|
|
31
|
+
has_gl_transaction
|
|
32
|
+
is_order_like
|
|
33
|
+
has_additional_events :amount_paid_change
|
|
34
|
+
|
|
35
|
+
belongs_to :sales_order, export: true
|
|
36
|
+
belongs_to :customer, export: true
|
|
37
|
+
belongs_to :location, export: true
|
|
38
|
+
belongs_to :terms, class_name: 'Skr::PaymentTerm', export: true
|
|
39
|
+
belongs_to :pick_ticket, inverse_of: :invoice, export: true
|
|
40
|
+
belongs_to :billing_address, class_name: 'Skr::Address', export: { writable: true }
|
|
41
|
+
belongs_to :shipping_address, class_name: 'Skr::Address', export: { writable: true }
|
|
42
|
+
|
|
43
|
+
has_many :gl_transactions, :as=>:source
|
|
44
|
+
|
|
45
|
+
has_many :lines, -> { order(:position) }, class_name: 'Skr::InvLine', inverse_of: :invoice,
|
|
46
|
+
extend: Concerns::INV::Lines, export: { writable: true }
|
|
47
|
+
|
|
48
|
+
before_save :maybe_mark_paid
|
|
49
|
+
|
|
50
|
+
before_validation :set_defaults, on: :create
|
|
51
|
+
|
|
52
|
+
validates :customer, :location, set: true
|
|
53
|
+
validate :ensure_location_matches_so
|
|
54
|
+
|
|
55
|
+
scope :open_for_customer, lambda{ | customer |
|
|
56
|
+
where([ "customer_id=? and state != 'paid'", customer.is_a?(Customer) ? customer.id : customer ])
|
|
57
|
+
}, export: true
|
|
58
|
+
|
|
59
|
+
scope :with_details, lambda { |should_use=true |
|
|
60
|
+
compose_query_using_detail_view( view: 'inv_details', join_to: 'invoice_id' )
|
|
61
|
+
}, export: true
|
|
62
|
+
|
|
63
|
+
state_machine do
|
|
64
|
+
state :open, initial: true
|
|
65
|
+
state :paid
|
|
66
|
+
state :partial
|
|
67
|
+
event :mark_paid do
|
|
68
|
+
transitions from: [:open,:partial], to: :paid
|
|
69
|
+
before :apply_balances
|
|
70
|
+
end
|
|
71
|
+
event :mark_partial do
|
|
72
|
+
transitions from: [:open,:partial], to: :partial
|
|
73
|
+
before :apply_balances
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def initialize(attributes = {})
|
|
78
|
+
super
|
|
79
|
+
self.invoice_date = Date.today
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @return [BigDecimal] total - amount_paid
|
|
83
|
+
def unpaid_amount
|
|
84
|
+
self.total - amount_paid
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @return [Boolean] is the invoice paid in full
|
|
88
|
+
def fully_paid?
|
|
89
|
+
unpaid_amount <= 0
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
# attributes for GlTransaction
|
|
95
|
+
def attributes_for_gl_transaction
|
|
96
|
+
{ location: location, source: self,
|
|
97
|
+
description: "INV #{self.visible_id}" }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# set the state if the amount_paid was changed
|
|
101
|
+
def maybe_mark_paid
|
|
102
|
+
return unless amount_paid_changed?
|
|
103
|
+
if self.fully_paid? && self.may_mark_paid?
|
|
104
|
+
self.state_event = 'mark_paid'
|
|
105
|
+
elsif self.amount_paid > 0 && self.may_mark_partial?
|
|
106
|
+
self.state_event = 'mark_partial'
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def apply_balances
|
|
111
|
+
return unless amount_paid_changed?
|
|
112
|
+
change = amount_paid - amount_paid_was
|
|
113
|
+
|
|
114
|
+
Skr::Core.logger.debug "Applying payment #{amount_paid} changed: #{change}"
|
|
115
|
+
|
|
116
|
+
return if change.zero?
|
|
117
|
+
|
|
118
|
+
GlTransaction.push_or_save(
|
|
119
|
+
owner: self, amount: change,
|
|
120
|
+
debit: customer.gl_receivables_account, credit: GlAccount.default_for(:deposit_holding)
|
|
121
|
+
)
|
|
122
|
+
fire_event( :amount_paid_change )
|
|
123
|
+
true
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def set_defaults
|
|
128
|
+
|
|
129
|
+
if pick_ticket
|
|
130
|
+
self.location = pick_ticket.location
|
|
131
|
+
self.sales_order = pick_ticket.sales_order
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if sales_order
|
|
135
|
+
self.terms ||= sales_order.terms
|
|
136
|
+
self.customer = sales_order.customer
|
|
137
|
+
self.po_num = sales_order.po_num if self.po_num.blank?
|
|
138
|
+
self.billing_address = sales_order.billing_address if self.billing_address.blank?
|
|
139
|
+
self.shipping_address = sales_order.shipping_address if self.shipping_address.blank?
|
|
140
|
+
self.options.merge!(sales_order.options)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
if customer
|
|
144
|
+
self.billing_address = customer.billing_address if self.billing_address.blank?
|
|
145
|
+
self.shipping_address = customer.shipping_address if self.shipping_address.blank?
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def ensure_location_matches_so
|
|
151
|
+
if sales_order && location != sales_order.location
|
|
152
|
+
self.errors.add(:location, "#{location.code} must match location that order was taken on (#{sales_order.location.code})")
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
end # Skr module
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
|
|
3
|
+
# A location that holds inventory
|
|
4
|
+
class Location < Skr::Model
|
|
5
|
+
|
|
6
|
+
has_code_identifier :from=>'name'
|
|
7
|
+
|
|
8
|
+
belongs_to :address, export: { writable: true }
|
|
9
|
+
|
|
10
|
+
has_many :sku_locs
|
|
11
|
+
|
|
12
|
+
validates :gl_branch_code, :presence => true, :numericality=>true, :length=>{:is=>2}
|
|
13
|
+
|
|
14
|
+
locked_fields :gl_branch_code
|
|
15
|
+
|
|
16
|
+
before_validation :set_defaults, :on=>:create
|
|
17
|
+
|
|
18
|
+
# @return [Location] the location that's specified by {Skr::Core::Configuration#default_location_code}
|
|
19
|
+
def self.default
|
|
20
|
+
Location.find_by_code( Skr.config.default_location_code )
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def set_defaults
|
|
26
|
+
self.gl_branch_code ||= Skr.config.default_branch_code
|
|
27
|
+
true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
|
|
3
|
+
# A pay
|
|
4
|
+
class PaymentTerm < Skr::Model
|
|
5
|
+
|
|
6
|
+
has_code_identifier
|
|
7
|
+
|
|
8
|
+
def discount
|
|
9
|
+
@discount_percnum ||= Core::Numbers::PercNum.new( read_attribute('discount_amount') )
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def discount_amount=(value)
|
|
13
|
+
@discount_percnum = nil
|
|
14
|
+
super(value)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def immediate?
|
|
18
|
+
self.days.nil? || self.days.zero?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def discount_expires_at( start_date = Date.today )
|
|
22
|
+
( start_date + self.discount_days.days ).to_date
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def due_date_from( start_date = Date.today )
|
|
26
|
+
( start_date + self.days.days ).to_date
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
|
|
3
|
+
class PickTicket < Skr::Model
|
|
4
|
+
|
|
5
|
+
has_visible_id
|
|
6
|
+
is_order_like
|
|
7
|
+
|
|
8
|
+
belongs_to :sales_order
|
|
9
|
+
belongs_to :invoice, inverse_of: :pick_ticket, listen: { create: 'on_invoice' }
|
|
10
|
+
belongs_to :location
|
|
11
|
+
|
|
12
|
+
has_one :customer, through: :sales_order, export: true
|
|
13
|
+
has_one :terms, through: :sales_order
|
|
14
|
+
|
|
15
|
+
has_many :lines, ->{ order(:position) }, class_name: 'Skr::PtLine', inverse_of: :pick_ticket,
|
|
16
|
+
extend: Concerns::PT::Lines, export: { writable: true }
|
|
17
|
+
|
|
18
|
+
scope :with_details, lambda { | *args |
|
|
19
|
+
compose_query_using_detail_view( view: 'pt_details', join_to: 'pick_tickets_id' )
|
|
20
|
+
}, export: true
|
|
21
|
+
|
|
22
|
+
delegate :bill_addr, :to=>:sales_order
|
|
23
|
+
|
|
24
|
+
# If true, the PickTicket (and it's lines) will be marked as complete once it's saved
|
|
25
|
+
whitelist_attributes :mark_complete
|
|
26
|
+
|
|
27
|
+
before_update :check_for_mark_completed
|
|
28
|
+
|
|
29
|
+
validates :sales_order, set: true
|
|
30
|
+
validates :lines, presence: true
|
|
31
|
+
export_methods :ship_addr, :bill_addr
|
|
32
|
+
|
|
33
|
+
def ship_addr
|
|
34
|
+
sales_order.ship_addr.blank? ? sales_order.bill_addr : sales_order.ship_addr
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def is_tax_exempt?
|
|
38
|
+
self.sales_order.is_tax_exempt?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def is_other_charge_locked?
|
|
42
|
+
return is_complete
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def cancel!
|
|
46
|
+
update_attributes({ :is_complete=> true })
|
|
47
|
+
lines.each do | line |
|
|
48
|
+
line.update_attributes :is_complete=>true
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def check_for_mark_completed
|
|
55
|
+
return unless self.mark_complete
|
|
56
|
+
assign_attributes :is_complete=>true
|
|
57
|
+
lines.each do | line |
|
|
58
|
+
line.update_attributes :is_complete=>true
|
|
59
|
+
end
|
|
60
|
+
true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def on_invoice(inv)
|
|
65
|
+
self.update_attributes is_complete: true, shipped_at: Time.now
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end # Skr module
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
|
|
3
|
+
class PoLine < Skr::Model
|
|
4
|
+
|
|
5
|
+
acts_as_uom
|
|
6
|
+
is_sku_loc_line parent: 'purchase_order'
|
|
7
|
+
|
|
8
|
+
belongs_to :purchase_order, :inverse_of=>:lines
|
|
9
|
+
belongs_to :sku_loc, export: true
|
|
10
|
+
belongs_to :sku_vendor, export: true
|
|
11
|
+
|
|
12
|
+
has_one :sku, :through => :sku_loc, export: true
|
|
13
|
+
|
|
14
|
+
has_many :receipts, class_name: 'Skr::PorLine',
|
|
15
|
+
:inverse_of=>:po_line, listen: { create: :update_qty_received! }
|
|
16
|
+
|
|
17
|
+
validates :purchase_order, :sku_loc, set: true
|
|
18
|
+
validates :description, :sku_code, presence: true
|
|
19
|
+
validates :qty, :price, numericality: true, allow_nil: false
|
|
20
|
+
|
|
21
|
+
locked_fields :qty, :qty_received
|
|
22
|
+
|
|
23
|
+
scope :incomplete, ->{ where('qty - qty_received - qty_canceled != 0' ) }
|
|
24
|
+
|
|
25
|
+
before_validation :set_defaults, :on=>:create
|
|
26
|
+
|
|
27
|
+
def qty_unreceived
|
|
28
|
+
qty - qty_received - qty_canceled
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def complete?
|
|
32
|
+
qty_unreceived.zero?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def update_qty_received!( receipt=nil )
|
|
36
|
+
unlock_fields :qty_received do
|
|
37
|
+
self.qty_received = receipts.sum(:qty)
|
|
38
|
+
self.save( :validate => false )
|
|
39
|
+
if self.complete?
|
|
40
|
+
self.purchase_order.set_maybe_completed!
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def set_defaults
|
|
48
|
+
if sku_loc && sku_vendor.nil?
|
|
49
|
+
self.sku_vendor = sku_loc.sku.sku_vendors.for_vendor( purchase_order.vendor )
|
|
50
|
+
elsif sku_vendor && sku_loc.nil?
|
|
51
|
+
self.sku_loc = sku_vendor.sku.sku_locs.find_or_create_for( purchase_order.location )
|
|
52
|
+
end
|
|
53
|
+
if sku_loc
|
|
54
|
+
self.sku_code ||= sku_loc.sku.code
|
|
55
|
+
self.description ||= sku_loc.sku.description
|
|
56
|
+
end
|
|
57
|
+
if sku_vendor
|
|
58
|
+
self.part_code ||= sku_vendor.part_code
|
|
59
|
+
self.price ||= sku_vendor.cost
|
|
60
|
+
self.uom_code ||= sku_vendor.uom_code
|
|
61
|
+
self.uom_size ||= sku_vendor.uom_size
|
|
62
|
+
end
|
|
63
|
+
true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end # Skr module
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
|
|
3
|
+
# Is a record of an inventory receipt
|
|
4
|
+
# A {PurchaseOrder} can have one or more of them
|
|
5
|
+
|
|
6
|
+
class PoReceipt < Skr::Model
|
|
7
|
+
|
|
8
|
+
has_visible_id
|
|
9
|
+
has_sku_loc_lines
|
|
10
|
+
has_gl_transaction
|
|
11
|
+
|
|
12
|
+
belongs_to :purchase_order, export: true
|
|
13
|
+
belongs_to :vendor, export: true
|
|
14
|
+
belongs_to :location, export: true
|
|
15
|
+
|
|
16
|
+
has_one :gl_transaction, :as=>:source
|
|
17
|
+
|
|
18
|
+
has_many :lines, :class_name=>'Skr::PorLine', export: { writable: true }, inverse_of: :po_receipt
|
|
19
|
+
|
|
20
|
+
validates :freight, numericality: true
|
|
21
|
+
validates :purchase_order, :location, presence: true
|
|
22
|
+
|
|
23
|
+
before_create :record_freight, if: ->{ freight.nonzero? }
|
|
24
|
+
after_create :logit
|
|
25
|
+
|
|
26
|
+
def purchase_order=(po)
|
|
27
|
+
super
|
|
28
|
+
self.location ||= purchase_order.location
|
|
29
|
+
self.vendor = purchase_order.vendor
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def attributes_for_gl_transaction
|
|
35
|
+
{ location: location, source: self,
|
|
36
|
+
description: "PO RECPT #{self.visible_id}" }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def logit
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def record_freight
|
|
43
|
+
GlTransaction.current.add_posting( amount: self.freight,
|
|
44
|
+
debit: GlAccount.default_for( :inventory_receipts_clearing ),
|
|
45
|
+
credit: vendor.gl_freight_account
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
|
|
3
|
+
class PorLine < Skr::Model
|
|
4
|
+
|
|
5
|
+
acts_as_uom
|
|
6
|
+
is_sku_loc_line parent: 'po_receipt'
|
|
7
|
+
|
|
8
|
+
belongs_to :po_receipt
|
|
9
|
+
belongs_to :sku_loc, export: true
|
|
10
|
+
belongs_to :po_line, export: true
|
|
11
|
+
belongs_to :sku_vendor, export: true
|
|
12
|
+
|
|
13
|
+
has_one :sku, :through => :sku_loc, export: true
|
|
14
|
+
|
|
15
|
+
has_many :sku_trans, :as=>:origin, validate: true
|
|
16
|
+
|
|
17
|
+
before_create :adjust_inventory
|
|
18
|
+
# after_update :adjust_gl
|
|
19
|
+
validates :qty, numericality: { greater_than: 0 }
|
|
20
|
+
|
|
21
|
+
whitelist_attributes :auto_allocate
|
|
22
|
+
|
|
23
|
+
def po_line=(pol)
|
|
24
|
+
super
|
|
25
|
+
%w{ sku_code part_code description uom_code uom_size }.each do | attr |
|
|
26
|
+
self[ attr ] = po_line[ attr ]
|
|
27
|
+
end
|
|
28
|
+
self.sku_loc = pol.sku_loc
|
|
29
|
+
self.sku_vendor = pol.sku_vendor
|
|
30
|
+
self.uom = pol.uom
|
|
31
|
+
self.price = pol.price
|
|
32
|
+
self.qty = pol.qty_unreceived
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def adjust_inventory
|
|
38
|
+
Core.logger.debug( "Receiving #{self.ea_qty} into stock" )
|
|
39
|
+
tran = self.sku_trans.build({
|
|
40
|
+
origin: self, qty: self.qty,
|
|
41
|
+
sku_loc: po_line.sku_loc,
|
|
42
|
+
origin_description: "PO #{self.po_line.purchase_order.visible_id}",
|
|
43
|
+
cost: self.extended_price, uom: self.uom,
|
|
44
|
+
credit_gl_account: self.sku.gl_asset_account,
|
|
45
|
+
debit_gl_account: GlAccount.default_for( :inventory_receipts_clearing )
|
|
46
|
+
})
|
|
47
|
+
if self.auto_allocate
|
|
48
|
+
tran.allocate_after_save = true
|
|
49
|
+
end
|
|
50
|
+
true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def ensure_qty_is_less_than_unreceived
|
|
54
|
+
if qty_changed? && po_line && qty > po_line.qty_unreceived
|
|
55
|
+
errors.add(:qty,"#{qty} must be less than unreceived qty of #{po_line.qty_unreceived}")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def adjust_gl
|
|
60
|
+
diff = price - price_was
|
|
61
|
+
return if diff.zero?
|
|
62
|
+
|
|
63
|
+
tran = self.sku_trans.build({ :origin=>self, :qty => 0, :sku_loc=>po_line.sku_loc,
|
|
64
|
+
:credit_gl_account => self.sku.gl_asset_account,
|
|
65
|
+
:uom_size=>self.uom_size, :uom_code=>self.uom_code })
|
|
66
|
+
|
|
67
|
+
tran.cost = diff * qty
|
|
68
|
+
|
|
69
|
+
tran.debit_gl_account = if voucher.confirmed?
|
|
70
|
+
voucher.vendor.gl_payables_account
|
|
71
|
+
else # otherwise it's in the clearing account
|
|
72
|
+
GlAccount.default_for( :inventory_receipts_clearing )
|
|
73
|
+
end
|
|
74
|
+
tran.save!
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
|
|
3
|
+
class PtLine < Skr::Model
|
|
4
|
+
|
|
5
|
+
acts_as_uom
|
|
6
|
+
is_sku_loc_line parent: 'pick_ticket'
|
|
7
|
+
|
|
8
|
+
attr_accessor :qty_to_ship
|
|
9
|
+
|
|
10
|
+
belongs_to :pick_ticket, export: true
|
|
11
|
+
belongs_to :sku_loc, export: true
|
|
12
|
+
belongs_to :so_line
|
|
13
|
+
|
|
14
|
+
has_one :inv_line, inverse_of: :pt_line, listen: { save: :update_from_inv_line }
|
|
15
|
+
has_one :sales_order, :through=>:pick_ticket, export: true
|
|
16
|
+
has_one :sku, :through => :sku_loc, export: true
|
|
17
|
+
|
|
18
|
+
scope :picking, ->{ where({ :is_complete=>false }) }
|
|
19
|
+
|
|
20
|
+
delegate_and_export_field :pick_ticket, :visible_id
|
|
21
|
+
delegate_and_export_field :sales_order, :visible_id
|
|
22
|
+
|
|
23
|
+
before_create :set_defaults#, on: :create
|
|
24
|
+
|
|
25
|
+
def cancel!
|
|
26
|
+
self.update_attributes! :is_complete=>true
|
|
27
|
+
self.pick_ticket.maybe_cancel
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def is_invoiceable?
|
|
31
|
+
! self.is_complete? && self.qty_to_ship.to_i > 0
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def total
|
|
35
|
+
self.price * ( self.qty_to_ship || self.qty )
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def update_from_inv_line( inv_line )
|
|
41
|
+
self.update_attributes( qty_invoiced: inv_line.qty )
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def set_defaults
|
|
45
|
+
|
|
46
|
+
if self.so_line.blank?
|
|
47
|
+
self.so_line = self.pick_ticket.sales_order.lines.where({ sku_loc_id: self.sku_loc_id }).first
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if self.so_line.blank?
|
|
51
|
+
self.so_line = self.pick_ticket.sales_order.lines.create({
|
|
52
|
+
sku_loc: self.sku_loc, qty: self.qty, price: self.price,
|
|
53
|
+
uom: self.uom, description: self.description
|
|
54
|
+
})
|
|
55
|
+
unless self.so_line.valid?
|
|
56
|
+
self.errors.add(:so_line, "cannot be created because #{self.so_line.errors.full_messages.join(', ')}" )
|
|
57
|
+
return false
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
self.sku_loc ||= so_line.sku_loc
|
|
62
|
+
self.bin ||= sku_loc.bin
|
|
63
|
+
self.price ||= so_line.price
|
|
64
|
+
self.sku_code = sku_loc.sku.code if self.sku_code.blank?
|
|
65
|
+
self.description = so_line.description if self.description.blank?
|
|
66
|
+
self.uom = so_line.uom if self.uom.blank?
|
|
67
|
+
true
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
end # Skr module
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
|
|
3
|
+
# A Purchase Order (often abbreviated as PO)
|
|
4
|
+
# is a record of a request to purchase a product from a Vendor.
|
|
5
|
+
# It contains one or more {Sku}, the quantity desired for each and the price offered for them.
|
|
6
|
+
#
|
|
7
|
+
# A Purchase Order progresses through set stages:
|
|
8
|
+
#
|
|
9
|
+
# * It starts of as "saved"
|
|
10
|
+
# This state indicates that the PO has been saved but no further action has been under-taken.
|
|
11
|
+
# * Once it has been sent to the Vendor, it transitions to Sent.
|
|
12
|
+
# * On reciept of a confirmation from the vendor it becomes Confirmed At this point the PO may be
|
|
13
|
+
# considered a binding agreement with the {Vendor}.
|
|
14
|
+
# * When the ordered SKUs are received, the PO will be marked as either Partial or Complete.
|
|
15
|
+
# * A {PoReceipt} is then created from the Purchase Order.
|
|
16
|
+
|
|
17
|
+
class PurchaseOrder < Skr::Model
|
|
18
|
+
|
|
19
|
+
has_visible_id
|
|
20
|
+
|
|
21
|
+
belongs_to :terms, export: true, class_name: 'Skr::PaymentTerm'
|
|
22
|
+
belongs_to :vendor, export: true
|
|
23
|
+
belongs_to :location, export: true
|
|
24
|
+
belongs_to :ship_addr, :class_name=>'Skr::Address', export: { writable: true }
|
|
25
|
+
|
|
26
|
+
has_many :lines, ->{ order(:position) }, :class_name=>'Skr::PoLine', :inverse_of=>:purchase_order, export: {writable:true}
|
|
27
|
+
has_many :receipts, class_name: 'Skr::PoReceipt'
|
|
28
|
+
|
|
29
|
+
before_validation :set_defaults, :on=>:create
|
|
30
|
+
|
|
31
|
+
validates :vendor, :location, :terms, :presence=>true
|
|
32
|
+
|
|
33
|
+
delegate_and_export :vendor_code, :vendor_name
|
|
34
|
+
delegate_and_export :location_code
|
|
35
|
+
delegate_and_export :terms_code, :terms_description
|
|
36
|
+
|
|
37
|
+
export_methods :total
|
|
38
|
+
|
|
39
|
+
blacklist_attributes :receiving_completed_at
|
|
40
|
+
|
|
41
|
+
after_save :set_maybe_completed!
|
|
42
|
+
|
|
43
|
+
export_join_tables :details
|
|
44
|
+
export_scope :with_details, lambda { |should_use=true |
|
|
45
|
+
joins('join po_details as details on details.purchase_order_id = purchase_orders.id')
|
|
46
|
+
.select('purchase_orders.*, details.*') if should_use
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export_scope :only_incoming, lambda { |should_use=true|
|
|
50
|
+
with_details.where("state !='received'") if should_use
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
state_machine do
|
|
54
|
+
state :open, initial: true
|
|
55
|
+
state :received
|
|
56
|
+
|
|
57
|
+
event :mark_received do
|
|
58
|
+
transitions from: :open, to: :received
|
|
59
|
+
before do
|
|
60
|
+
self.receiving_completed_at = Time.now
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def after_message_transmitted( msg )
|
|
66
|
+
self.mark_transmitted if self.can_mark_transmitted?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def set_maybe_completed!
|
|
70
|
+
self.mark_received! unless received? or self.lines.incomplete.any?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def to_shipping_address
|
|
74
|
+
Address.copy_from(self,'ship_')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def set_message_fields( msg )
|
|
78
|
+
msg.recipient = self.vendor.payments_address.email_with_name if msg.recipient.blank?
|
|
79
|
+
msg.subject = "Purchase Order # #{self.visible_id}"
|
|
80
|
+
attach = msg.attachments.build
|
|
81
|
+
attach.set_type_to_pdf
|
|
82
|
+
pdf = self.as_pdf
|
|
83
|
+
attach.file = pdf
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def as_pdf( opts={} )
|
|
87
|
+
defaults = { :purchase_order => self, :include_received=>false }
|
|
88
|
+
Latex::Runner.new( 'purchase_order', defaults.merge( opts ) ).pdf( "PurchaseOrder_#{self.visible_id}.pdf" )
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def total
|
|
92
|
+
if total = self.read_attribute('total')
|
|
93
|
+
BigDecimal.new(total)
|
|
94
|
+
elsif self.association(:lines).loaded?
|
|
95
|
+
self.lines.inject(0){|sum,line| sum + line.total }
|
|
96
|
+
else
|
|
97
|
+
BigDecimal.new( self.lines.sum('price*qty') )
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def set_defaults
|
|
104
|
+
self.location ||= Location.default
|
|
105
|
+
self.order_date ||= Date.today
|
|
106
|
+
self.terms ||= self.vendor.terms if self.vendor
|
|
107
|
+
self.ship_addr = self.location.address if self.ship_addr.blank? && self.location
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
end # Skr module
|