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,84 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
module Concerns
|
|
3
|
+
|
|
4
|
+
# @see ClassMethods
|
|
5
|
+
module LockedFields
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
# Mark fields as locked, meaning they cannot be updated by using the
|
|
10
|
+
# regular attribute update methods. Instead it must be called in an unlock block
|
|
11
|
+
# Relies on attr_readonly internally
|
|
12
|
+
#
|
|
13
|
+
# Is used to designate sensitive fields that we want to make sure someone's thought about before updating
|
|
14
|
+
# Also solves the age old single equals vs double equals bug/typo.
|
|
15
|
+
#
|
|
16
|
+
# class BankAccount < Skr::Model
|
|
17
|
+
#
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# bank=BankAccount.find(1)
|
|
21
|
+
# b.mark_as_super if bank.account_balance = 42 # a bit contrived, but you get the idea
|
|
22
|
+
# b.save # oops, what's our balance now?
|
|
23
|
+
#
|
|
24
|
+
# Now let's try it again with locked_fields
|
|
25
|
+
#
|
|
26
|
+
# class BankAccount < Skr::Model
|
|
27
|
+
# attr_readonly :account_balance
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# bank=BankAccount.find(1)
|
|
31
|
+
# b.mark_as_super if bank.account_balance = 42
|
|
32
|
+
# b.save # Still not ideal since we marked the bank as super, but at least our balance is ok
|
|
33
|
+
#
|
|
34
|
+
# To update the balance we'd need to:
|
|
35
|
+
#
|
|
36
|
+
# b.unlock_fields( :account_balance ) do
|
|
37
|
+
# b.account_balance += 33
|
|
38
|
+
# end
|
|
39
|
+
# b.save
|
|
40
|
+
#
|
|
41
|
+
# This is still a bit contrived since we'd actually have
|
|
42
|
+
# an audit logger that would be involved and it'd be inside a transaction.
|
|
43
|
+
|
|
44
|
+
def locked_fields( *flds )
|
|
45
|
+
include InstanceMethods
|
|
46
|
+
attr_readonly( *flds )
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def has_locks( *locks )
|
|
50
|
+
locks.each do | lock |
|
|
51
|
+
define_method( "unlock_#{lock}" ) do | &block |
|
|
52
|
+
instance_variable_set "@_lock_#{lock}_unlocked", true
|
|
53
|
+
block.call
|
|
54
|
+
remove_instance_variable "@_lock_#{lock}_unlocked"
|
|
55
|
+
end
|
|
56
|
+
define_method( "is_#{lock}_unlocked?") do
|
|
57
|
+
instance_variable_defined? "@_lock_#{lock}_unlocked"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
module InstanceMethods
|
|
65
|
+
# Unlock the field for updates inside the block
|
|
66
|
+
# yields, then restores it.
|
|
67
|
+
# Is class wide, meaning it Will temporarily open all instances of the class up for access in a threaded environment
|
|
68
|
+
def unlock_fields( *flds, &block )
|
|
69
|
+
attr_syms = flds.map(&:to_s)
|
|
70
|
+
|
|
71
|
+
self.class.attr_readonly.subtract( attr_syms )
|
|
72
|
+
|
|
73
|
+
yield
|
|
74
|
+
|
|
75
|
+
attr_syms.each do | fld |
|
|
76
|
+
self.class.attr_readonly.add( fld )
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
module Concerns
|
|
3
|
+
|
|
4
|
+
module PT
|
|
5
|
+
|
|
6
|
+
module Lines
|
|
7
|
+
|
|
8
|
+
def set_ship_qty
|
|
9
|
+
each{|l| l.qty_to_ship = l.qty }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def ea_picking_qty
|
|
13
|
+
if proxy_association.loaded?
|
|
14
|
+
inject(0){ | sum, ptl | sum+(ptl.qty*ptl.uom_size) unless ptl.is_complete? }
|
|
15
|
+
else
|
|
16
|
+
where( is_complete: false ).sum('qty*uom_size')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
module Concerns
|
|
3
|
+
|
|
4
|
+
# @see ClassMethods
|
|
5
|
+
module RandomHashCode
|
|
6
|
+
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
# ### Random Hash Code Concern
|
|
10
|
+
# This adds the {#has_random_hash_code} class method
|
|
11
|
+
module ClassMethods
|
|
12
|
+
|
|
13
|
+
# A random string that identifies an entity, such as a Customer, or Vendor
|
|
14
|
+
# The code is generated by {Skr::Strings.random} for new records
|
|
15
|
+
# It's useful for generating *magic* links for access to an entity that cannot be guessed.
|
|
16
|
+
# @param field_name [Symbol] which field should the hash_code be stored in
|
|
17
|
+
# @param length [Integer] how long the hash_code should be
|
|
18
|
+
|
|
19
|
+
def has_random_hash_code( field_name: :hash_code, length: 12 )
|
|
20
|
+
|
|
21
|
+
validates field_name, :presence=>{
|
|
22
|
+
:message=>"hash code is not set (should be automatically chosen)"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
scope :with_hash_code, lambda{ | code |
|
|
26
|
+
where({ :hash_code=>code })
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
before_validation(:on=>:create) do
|
|
30
|
+
self[ field_name ] = Lanes::Strings.random( length ) if self[ field_name ].blank?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
module Concerns
|
|
3
|
+
|
|
4
|
+
module SO
|
|
5
|
+
|
|
6
|
+
module Lines
|
|
7
|
+
|
|
8
|
+
def set_ship_qty
|
|
9
|
+
each{|l| l.qty_to_ship = l.qty }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def eq_qty
|
|
13
|
+
if proxy_association.loaded?
|
|
14
|
+
inject(0){ | sum, sol | sum + (sol.eq_qty*uom_size) }
|
|
15
|
+
else
|
|
16
|
+
sum('qty*uom_size')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def eq_qty_allocated
|
|
21
|
+
if proxy_association.loaded?
|
|
22
|
+
inject(0){ | sum, sol | sum + (sol.qty_allocated * uom_size) }
|
|
23
|
+
else
|
|
24
|
+
sum('qty_allocated*uom_size')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'aasm' # Acts As State Machine
|
|
2
|
+
|
|
3
|
+
module Skr
|
|
4
|
+
module Concerns
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Models that use the {https://github.com/aasm/aasm}
|
|
8
|
+
# gem to track object state
|
|
9
|
+
module StateMachine
|
|
10
|
+
|
|
11
|
+
extend ActiveSupport::Concern
|
|
12
|
+
|
|
13
|
+
module ClassMethods
|
|
14
|
+
|
|
15
|
+
# Mark class as a StateMachine {https://github.com/aasm/aasm}
|
|
16
|
+
#
|
|
17
|
+
# Specifically it:
|
|
18
|
+
#
|
|
19
|
+
# * Adds the methods in {InstanceMethods} to the class
|
|
20
|
+
# * Blacklists the "state" field so it cannot be set via the API
|
|
21
|
+
# * Allows access to the "state_event" pseudo field from the API
|
|
22
|
+
# * Sets up the aasm library with the contents of &block
|
|
23
|
+
def state_machine( options={}, &block )
|
|
24
|
+
include InstanceMethods
|
|
25
|
+
include AASM
|
|
26
|
+
aasm( options.merge( column: 'state' ), &block )
|
|
27
|
+
|
|
28
|
+
whitelist_attributes :state_event
|
|
29
|
+
|
|
30
|
+
export_methods :valid_state_events, :optional=>false
|
|
31
|
+
blacklist_attributes :state
|
|
32
|
+
|
|
33
|
+
before_save :fire_state_machine_event_on_save
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module InstanceMethods
|
|
39
|
+
|
|
40
|
+
def fire_state_machine_event_on_save
|
|
41
|
+
return unless state_event.present?
|
|
42
|
+
event_name = state_event.to_sym
|
|
43
|
+
if valid_state_events.include?( event_name )
|
|
44
|
+
self.send( :aasm_fire_event, event_name, {:persist=>false} )
|
|
45
|
+
else
|
|
46
|
+
errors.add(:state_event, "is not valid")
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [Array of symbols] the available state_transistions
|
|
52
|
+
def valid_state_events
|
|
53
|
+
aasm.events
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
module Concerns
|
|
3
|
+
|
|
4
|
+
# @see ClassMethods
|
|
5
|
+
module VisibleIdIdentifier
|
|
6
|
+
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
module InstanceMethods
|
|
10
|
+
|
|
11
|
+
# setup the visible id to the next available #{Skr::SequentialId}
|
|
12
|
+
# @return [Integer] the assigned ID
|
|
13
|
+
def assign_visible_id!
|
|
14
|
+
self.visible_id = Skr::SequentialId.next_for( self.class )
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ### Visible ID Identifier Concern
|
|
20
|
+
# This adds the {#has_visible_id} class methods
|
|
21
|
+
module ClassMethods
|
|
22
|
+
|
|
23
|
+
# An auto-incrementing number that's user-visible.
|
|
24
|
+
# The visible_id is stored as an integer, but a string index is generated for
|
|
25
|
+
# querying by the sql like operator. The **with_visible_id** scope is available for this purpose
|
|
26
|
+
#
|
|
27
|
+
# The next number an also be adjusted by the end-user by setting {Skr::SequentialId}
|
|
28
|
+
# so they can set the numbers to start at
|
|
29
|
+
# a specific point, which is useful for getting Invoice and other
|
|
30
|
+
# numbers to match up to a legacy system
|
|
31
|
+
def has_visible_id
|
|
32
|
+
include InstanceMethods
|
|
33
|
+
validates :visible_id, :presence=>{
|
|
34
|
+
:message=>"ID was not set (should be automatically chosen)"
|
|
35
|
+
}
|
|
36
|
+
alias_attribute :record_identifier, :visible_id
|
|
37
|
+
before_validation :assign_visible_id!, :on=>:create
|
|
38
|
+
|
|
39
|
+
export_scope :with_visible_id, lambda{ | visid |
|
|
40
|
+
if visid.to_s =~/%/
|
|
41
|
+
where( 'cast(visible_id as varchar) like ?', visid.to_s )
|
|
42
|
+
else
|
|
43
|
+
where( 'cast(visible_id as varchar) = ?', visid.to_s )
|
|
44
|
+
end
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'lanes/configuration'
|
|
2
|
+
require_relative 'standard_pricing_provider'
|
|
3
|
+
|
|
4
|
+
module Skr
|
|
5
|
+
|
|
6
|
+
class Configuration < Lanes::Configuration
|
|
7
|
+
|
|
8
|
+
# Database tables will have this prefix applied to them
|
|
9
|
+
config_option :table_prefix, 'skr_'
|
|
10
|
+
|
|
11
|
+
# The GL branch code to use for default newly created locations
|
|
12
|
+
config_option :default_branch_code, '01'
|
|
13
|
+
|
|
14
|
+
# The string value of the UserModel. Will be set on model's updated_by and created_by
|
|
15
|
+
config_option :user_model, 'UserProxy'
|
|
16
|
+
|
|
17
|
+
# Transactions that do not specify a location will use the one that's identified by this code
|
|
18
|
+
config_option :default_location_code, 'DEFAULT'
|
|
19
|
+
|
|
20
|
+
# Do freshly created SKUs default to being backorderable?
|
|
21
|
+
config_option :skus_backorder_default, true
|
|
22
|
+
|
|
23
|
+
# The code for a Sku that represents tax
|
|
24
|
+
config_option :tax_sku_code, 'TAX'
|
|
25
|
+
|
|
26
|
+
# Code for a Sku that represents shipping charges
|
|
27
|
+
config_option :ship_sku_code, 'SHIP'
|
|
28
|
+
|
|
29
|
+
# Code for a PaymentTerm that will be used as the default for new Customers
|
|
30
|
+
config_option :customer_terms_code, 'CASH'
|
|
31
|
+
|
|
32
|
+
# Code for a PaymentTerm that will be used as the default for new Vendors
|
|
33
|
+
config_option :vendor_terms_code, 'CASH'
|
|
34
|
+
|
|
35
|
+
config_option :pricing_provider, Skr::StandardPricingProvider
|
|
36
|
+
|
|
37
|
+
config_option :default_gl_accounts, {
|
|
38
|
+
# The Accounts Receivable (AR) GL account number to use for freshly created Customers
|
|
39
|
+
ar: '1200',
|
|
40
|
+
# The Accounts Payable (AP) GL account number to use for freshly created Vendors
|
|
41
|
+
ap: '2200',
|
|
42
|
+
# The Freight GL account number to use for freshly created Vendors
|
|
43
|
+
freight: '6420',
|
|
44
|
+
# The Asset GL account number to use for freshly created SKUs
|
|
45
|
+
asset: '1100',
|
|
46
|
+
# Clearing account for inventory that's been
|
|
47
|
+
inventory_receipts_clearing: '2600',
|
|
48
|
+
# Holding account for funds that are awaiting deposit
|
|
49
|
+
deposit_holding: '1010'
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class << self
|
|
56
|
+
@@config = Configuration.new
|
|
57
|
+
def config
|
|
58
|
+
@@config
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def configure
|
|
62
|
+
yield(@@config)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
require 'lanes/configuration'
|
|
2
|
+
|
|
3
|
+
module Skr
|
|
4
|
+
|
|
5
|
+
module DB
|
|
6
|
+
|
|
7
|
+
module TableFields
|
|
8
|
+
|
|
9
|
+
def skr_code_identifier
|
|
10
|
+
column( :code, :string, :null=>false )
|
|
11
|
+
skr_extra_indexes['code'] = {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def skr_visible_id
|
|
15
|
+
column( :visible_id, :integer, :null=>false )
|
|
16
|
+
skr_extra_indexes['visible_id'] = {
|
|
17
|
+
function: 'CAST(visible_id AS VARCHAR)'
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# track modifications
|
|
22
|
+
def skr_track_modifications( create_only: false )
|
|
23
|
+
column( :created_at, :datetime, :null=>false )
|
|
24
|
+
column( :created_by_id, :integer, :null=>false )
|
|
25
|
+
unless create_only
|
|
26
|
+
column( :updated_at, :datetime, :null=>false )
|
|
27
|
+
column( :updated_by_id, :integer, :null=>false )
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def skr_currency( names, options )
|
|
32
|
+
options[ :precision ] ||= 15
|
|
33
|
+
options[ :scale ] ||= 2
|
|
34
|
+
column( names, :decimal, options )
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# An skr_reference combines a belongs_to / has_one column
|
|
38
|
+
# with a postgresql foreign key reference.
|
|
39
|
+
def skr_reference( to_table, *args )
|
|
40
|
+
options = args.extract_options!
|
|
41
|
+
|
|
42
|
+
options[:column] ||= to_table.to_s + '_id'
|
|
43
|
+
|
|
44
|
+
column( options[:column], :integer, :null=>options[:null] || false )
|
|
45
|
+
to_table = options[:to_table] if options.has_key? :to_table
|
|
46
|
+
|
|
47
|
+
if options[:single]
|
|
48
|
+
to_table = to_table.to_s.pluralize
|
|
49
|
+
end
|
|
50
|
+
skr_foreign_keys[ to_table.to_sym ] = options
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def skr_foreign_keys
|
|
54
|
+
@skr_foreign_keys ||= {}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def skr_extra_indexes
|
|
58
|
+
@skr_extra_indexs ||= {}
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
module MigrationMethods
|
|
63
|
+
|
|
64
|
+
def create_skr_table(table_name, *args, &block)
|
|
65
|
+
definition = nil
|
|
66
|
+
create_table( Skr.config.table_prefix + table_name, *args ) do | td |
|
|
67
|
+
# Thanks for the trick from the Foreigner gem!
|
|
68
|
+
# in connection_adapters/abstract/schema_statements
|
|
69
|
+
definition = td
|
|
70
|
+
block.call(td) unless block.nil?
|
|
71
|
+
end
|
|
72
|
+
definition.skr_foreign_keys.each do |to_table, options |
|
|
73
|
+
skr_add_foreign_key( table_name, to_table, options )
|
|
74
|
+
end
|
|
75
|
+
definition.skr_extra_indexes.each do | index_column, options |
|
|
76
|
+
skr_add_index( table_name, index_column, options )
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def skr_add_index( table_name, columns, options={} )
|
|
81
|
+
table_name = Skr.config.table_prefix + table_name.to_s
|
|
82
|
+
if options[:function]
|
|
83
|
+
unique = options[:unique] ? 'unique' : ''
|
|
84
|
+
name = table_name + 'indx_' + columns
|
|
85
|
+
execute( "create #{unique} index #{name} on #{table_name}(#{options[:function]})" )
|
|
86
|
+
else
|
|
87
|
+
add_index( table_name, columns, options )
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def skr_add_foreign_key( table_name, to_table, options = {} )
|
|
92
|
+
from_table = Skr.config.table_prefix + table_name.to_s
|
|
93
|
+
to_table = Skr.config.table_prefix + to_table.to_s
|
|
94
|
+
|
|
95
|
+
column = options[:column] || "#{to_table.to_s.singularize}_id"
|
|
96
|
+
foreign_key_name = options.key?(:name) ? options[:name].to_s : "#{from_table}_#{column}_fk"
|
|
97
|
+
|
|
98
|
+
primary_key = options[:primary_key] || "id"
|
|
99
|
+
dependency = case options[:dependent]
|
|
100
|
+
when :nullify then "ON DELETE SET NULL"
|
|
101
|
+
when :delete then "ON DELETE CASCADE"
|
|
102
|
+
when :restrict then "ON DELETE RESTRICT"
|
|
103
|
+
else ""
|
|
104
|
+
end
|
|
105
|
+
sql = "ALTER TABLE #{quote_table_name(from_table)} " +
|
|
106
|
+
"ADD CONSTRAINT #{quote_column_name(foreign_key_name)} " +
|
|
107
|
+
"FOREIGN KEY (#{quote_column_name(column)}) " +
|
|
108
|
+
"REFERENCES #{quote_table_name( to_table )}(#{primary_key})"
|
|
109
|
+
sql << " #{dependency}" if dependency.present?
|
|
110
|
+
sql << " #{options[:options]}" if options[:options]
|
|
111
|
+
|
|
112
|
+
execute(sql)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def drop_skr_table( table_name, *args )
|
|
116
|
+
drop_table( Skr.config.table_prefix + table_name )
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def remove_skr_index( table_name, column )
|
|
120
|
+
remove_index( Skr.config.table_prefix + table_name, column )
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
module CommandRecorder
|
|
126
|
+
def create_skr_table(*args)
|
|
127
|
+
record(:create_skr_table, args)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def drop_skr_table(*args)
|
|
131
|
+
record(:drop_skr_table, args)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def invert_create_skr_table(args)
|
|
135
|
+
from_table, to_table, add_options = *args
|
|
136
|
+
add_options ||= {}
|
|
137
|
+
if add_options[:name]
|
|
138
|
+
options = {name: add_options[:name]}
|
|
139
|
+
elsif add_options[:column]
|
|
140
|
+
options = {column: add_options[:column]}
|
|
141
|
+
else
|
|
142
|
+
options = to_table
|
|
143
|
+
end
|
|
144
|
+
[:drop_skr_table, [from_table, options]]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def skr_add_index(*args)
|
|
148
|
+
record(:skr_add_index,args)
|
|
149
|
+
end
|
|
150
|
+
def invert_skr_add_index(args)
|
|
151
|
+
table, column = *args
|
|
152
|
+
[:remove_skr_index, [table, column]]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class ActiveRecord::Migration
|
|
163
|
+
def skr_prefix
|
|
164
|
+
Skr.config.table_prefix
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
ActiveRecord::Migration::CommandRecorder.class_eval do
|
|
169
|
+
include Skr::DB::CommandRecorder
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
|
|
173
|
+
include Skr::DB::MigrationMethods
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
ActiveRecord::ConnectionAdapters::TableDefinition.class_eval do
|
|
177
|
+
include Skr::DB::TableFields
|
|
178
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'lanes/access/extension'
|
|
2
|
+
|
|
3
|
+
module Skr
|
|
4
|
+
|
|
5
|
+
class Extension < Lanes::Extensions::Definition
|
|
6
|
+
|
|
7
|
+
identifier "skr"
|
|
8
|
+
self.uses_pub_sub = true
|
|
9
|
+
root_path Pathname.new(__FILE__).dirname.join("..","..").expand_path
|
|
10
|
+
components "record-finder", "select-field"
|
|
11
|
+
|
|
12
|
+
def on_boot
|
|
13
|
+
Lanes::API::Root.before do
|
|
14
|
+
Thread.current[:demo_user_info] = {
|
|
15
|
+
name: session[:name],
|
|
16
|
+
email: session[:email]
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
data/lib/skr/model.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
|
|
3
|
+
class Model < Lanes::Model
|
|
4
|
+
self.abstract_class = true
|
|
5
|
+
|
|
6
|
+
include Concerns::ActsAsUOM
|
|
7
|
+
include Concerns::StateMachine
|
|
8
|
+
include Concerns::HasSkuLocLines
|
|
9
|
+
include Concerns::HasGlTransaction
|
|
10
|
+
include Concerns::IsOrderLike
|
|
11
|
+
include Concerns::IsSkuLocLine
|
|
12
|
+
include Concerns::ImmutableModel
|
|
13
|
+
include Concerns::VisibleIdIdentifier
|
|
14
|
+
include Concerns::LockedFields
|
|
15
|
+
include Concerns::CodeIdentifier
|
|
16
|
+
include Concerns::RandomHashCode
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
|
|
3
|
+
# A postal address with optional email and phone number
|
|
4
|
+
# By default all fields may be left blank.
|
|
5
|
+
#
|
|
6
|
+
# Validations may be selectively enabled by using the #enable_validations method
|
|
7
|
+
class Address < Skr::Model
|
|
8
|
+
|
|
9
|
+
# @!attribute email
|
|
10
|
+
# The email address to use
|
|
11
|
+
|
|
12
|
+
# @!attribute ensure_not_blank
|
|
13
|
+
# Must the Address be filled out? The {#blank?} method must return false
|
|
14
|
+
# @return [Boolean]
|
|
15
|
+
# @!attribute validate_email
|
|
16
|
+
# Will the email be validated to meet the {EmailValidator} requirements?
|
|
17
|
+
# @return [Boolean]
|
|
18
|
+
# @!attribute validate_phone
|
|
19
|
+
# Should the phone field be validated to be not blank?
|
|
20
|
+
# @return [Boolean]
|
|
21
|
+
attr_accessor :ensure_not_blank, :validate_email, :validate_phone
|
|
22
|
+
|
|
23
|
+
validates :name, :line1, :city, :state, :postal_code, :presence=>true, :if=>:ensure_not_blank
|
|
24
|
+
validates :email, :presence=>true, :email=>true, :if=>:validate_email
|
|
25
|
+
validates :phone, :presence=>true, :if=>:validate_phone
|
|
26
|
+
|
|
27
|
+
# @return [Address] a blank copy of an address
|
|
28
|
+
def self.blank
|
|
29
|
+
Address.new({ name: '', line1: '', city: '', state: '', postal_code: '' })
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [String] the name and email formatted for inclusion in an email
|
|
33
|
+
def email_with_name
|
|
34
|
+
"#{name} <#{email}>"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# fill in missing fields from postal_code using the ZipCode lookup table
|
|
38
|
+
def fill_missing_from_zip
|
|
39
|
+
if ( self.postal_code.present? &&
|
|
40
|
+
( self.city.blank? || self.state.blank? ) &&
|
|
41
|
+
zc = ZipCode.find_by_code( self.postal_code )
|
|
42
|
+
)
|
|
43
|
+
self.city ||= zc.city
|
|
44
|
+
self.state ||= zc.state
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [Boolean] is any of (name line1 city state postal_code) blank?
|
|
49
|
+
def blank?
|
|
50
|
+
return !! %w{ name line1 city state postal_code }.detect{ |field| self[field].blank? }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# split the name on space
|
|
54
|
+
# @return [Hash]
|
|
55
|
+
# * :first [String] Portion of name before first space
|
|
56
|
+
# * :last [String] Portion of name after last space
|
|
57
|
+
def seperated_name
|
|
58
|
+
{:first=>name.to_s.split(' ').first, :last=> name.to_s.split(' ').last }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# enable selected validations
|
|
62
|
+
# @option options [Boolean] :include_email should the email be validated
|
|
63
|
+
# @option options [Boolean] :include_phone should the phone number be validated
|
|
64
|
+
def enable_validations( options = {} )
|
|
65
|
+
self.ensure_not_blank = true
|
|
66
|
+
self.validate_email = options[:include_email]
|
|
67
|
+
self.validate_phone = options[:include_phone]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @param include [Array] list of extra fields to include in the address
|
|
71
|
+
# @return [String] Address converted to string, formatted with line breaks in the typical US style of display
|
|
72
|
+
# @example
|
|
73
|
+
# address = Address.new( name: 'Bob\s Uncle',phone: '877-555-5555', line1: 'PO Box 87',
|
|
74
|
+
# city: 'Nowhereville', state: 'Urgandishly' postal_code: 'ASCN 1ZZ' )
|
|
75
|
+
# address.to_s( :include => :phone ) #=>
|
|
76
|
+
# Bob's Uncle
|
|
77
|
+
# PO Box 87
|
|
78
|
+
# Nowhereville, Urgandishly ASCN 1ZZ
|
|
79
|
+
# 877-5550-5555
|
|
80
|
+
def to_s( include: [] )
|
|
81
|
+
ret = ""
|
|
82
|
+
%w{ name line1 line2 }.each{ |a| ret << self[a] + "\n" unless self[a].blank? }
|
|
83
|
+
ret << city.to_s
|
|
84
|
+
ret << ' ' + state unless state.blank?
|
|
85
|
+
ret << ', ' + postal_code.to_s unless postal_code.blank?
|
|
86
|
+
include = [ *include ]
|
|
87
|
+
if include.any?
|
|
88
|
+
ret << "\n" + include.map{ | field | self[ field ] }.join(' ')
|
|
89
|
+
end
|
|
90
|
+
ret
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
end # end Skr module
|