stockor 0.1.9 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (313) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +254 -72
  4. data/Guardfile +1 -1
  5. data/README.md +24 -3
  6. data/app.json +28 -0
  7. data/client/skr/Extension.coffee +18 -0
  8. data/client/skr/components/BankAccountFinder.cjsx +34 -0
  9. data/client/skr/components/Currency.cjsx +19 -0
  10. data/client/skr/components/CustomerFinder.cjsx +37 -0
  11. data/client/skr/components/CustomerLink.cjsx +13 -0
  12. data/client/skr/components/CustomerProjectFinder.cjsx +49 -0
  13. data/client/skr/components/GlAccountChooser.cjsx +38 -0
  14. data/client/skr/components/InvoiceFinder.cjsx +37 -0
  15. data/client/skr/components/InvoiceLink.cjsx +13 -0
  16. data/client/skr/components/LocationChooser.cjsx +37 -0
  17. data/client/skr/components/PaymentCategoryFinder.cjsx +34 -0
  18. data/client/skr/components/PrintFormChooser.cjsx +29 -0
  19. data/client/skr/components/SalesOrderFinder.cjsx +38 -0
  20. data/client/skr/components/ScreenControls.cjsx +20 -0
  21. data/client/skr/components/SkuFinder.cjsx +30 -0
  22. data/client/skr/components/SkuLines.cjsx +76 -0
  23. data/client/skr/components/SystemSettings.cjsx +26 -0
  24. data/client/skr/components/TermsChooser.cjsx +40 -0
  25. data/client/skr/components/ToolbarButton.cjsx +7 -0
  26. data/client/skr/components/TotalsLine.cjsx +21 -0
  27. data/client/skr/components/TriStateIcon.cjsx +12 -0
  28. data/client/skr/components/UOMChooser.cjsx +24 -0
  29. data/client/skr/components/UserPreferences.cjsx +30 -0
  30. data/client/skr/components/VendorFinder.cjsx +40 -0
  31. data/client/skr/components/address/Address.cjsx +10 -10
  32. data/client/skr/components/styles.scss +16 -0
  33. data/client/skr/index.js +3 -1
  34. data/client/skr/models/Address.coffee +9 -1
  35. data/client/skr/models/BankAccount.coffee +17 -0
  36. data/client/skr/models/Base.coffee +6 -0
  37. data/client/skr/models/Customer.coffee +17 -10
  38. data/client/skr/models/CustomerProject.coffee +29 -0
  39. data/client/skr/models/GlAccount.coffee +22 -10
  40. data/client/skr/models/GlManualEntry.coffee +4 -2
  41. data/client/skr/models/GlPeriod.coffee +4 -6
  42. data/client/skr/models/GlPosting.coffee +7 -7
  43. data/client/skr/models/GlTransaction.coffee +3 -3
  44. data/client/skr/models/IaLine.coffee +9 -9
  45. data/client/skr/models/IaReason.coffee +4 -4
  46. data/client/skr/models/InvLine.coffee +30 -11
  47. data/client/skr/models/InventoryAdjustment.coffee +8 -6
  48. data/client/skr/models/Invoice.coffee +89 -18
  49. data/client/skr/models/Location.coffee +27 -6
  50. data/client/skr/models/Payment.coffee +36 -0
  51. data/client/skr/models/PaymentCategory.coffee +12 -0
  52. data/client/skr/models/PaymentTerm.coffee +17 -4
  53. data/client/skr/models/PickTicket.coffee +7 -5
  54. data/client/skr/models/PoLine.coffee +15 -15
  55. data/client/skr/models/PoReceipt.coffee +8 -6
  56. data/client/skr/models/PorLine.coffee +11 -11
  57. data/client/skr/models/PricingProvider.coffee +6 -0
  58. data/client/skr/models/PtLine.coffee +13 -13
  59. data/client/skr/models/PurchaseOrder.coffee +11 -9
  60. data/client/skr/models/SalesOrder.coffee +59 -22
  61. data/client/skr/models/Sku.coffee +17 -15
  62. data/client/skr/models/SkuLoc.coffee +14 -9
  63. data/client/skr/models/SkuTran.coffee +10 -10
  64. data/client/skr/models/SkuVendor.coffee +9 -9
  65. data/client/skr/models/SoLine.coffee +27 -20
  66. data/client/skr/models/TimeEntry.coffee +75 -0
  67. data/client/skr/models/Uom.coffee +27 -6
  68. data/client/skr/models/Vendor.coffee +14 -12
  69. data/client/skr/models/VoLine.coffee +11 -11
  70. data/client/skr/models/Voucher.coffee +7 -5
  71. data/client/skr/models/mixins/HasVisibleId.coffee +7 -0
  72. data/client/skr/models/mixins/PrintSupport.coffee +6 -0
  73. data/client/skr/models/mixins/SkuLine.coffee +61 -0
  74. data/client/skr/screens/Commands.coffee +29 -0
  75. data/client/skr/screens/bank-maint/BankMaint.cjsx +43 -0
  76. data/client/skr/screens/bank-maint/index.js +5 -0
  77. data/client/skr/screens/chart-of-accounts/ChartOfAccounts.cjsx +40 -0
  78. data/client/skr/screens/chart-of-accounts/index.js +1 -0
  79. data/client/skr/screens/chart-of-accounts/index.scss +12 -0
  80. data/client/skr/screens/customer-maint/CustomerMaint.cjsx +35 -36
  81. data/client/skr/screens/customer-projects/CustomerProjects.cjsx +70 -0
  82. data/client/skr/screens/customer-projects/index.js +5 -0
  83. data/client/skr/screens/customer-projects/index.scss +25 -0
  84. data/client/skr/screens/fresh-books-import/ApiInfo.cjsx +35 -0
  85. data/client/skr/screens/fresh-books-import/ChooseRecords.cjsx +116 -0
  86. data/client/skr/screens/fresh-books-import/FreshBooksImport.cjsx +31 -0
  87. data/client/skr/screens/fresh-books-import/Import.coffee +52 -0
  88. data/client/skr/screens/fresh-books-import/ViewRecords.cjsx +96 -0
  89. data/client/skr/screens/fresh-books-import/index.js +1 -0
  90. data/client/skr/screens/fresh-books-import/index.scss +39 -0
  91. data/client/skr/screens/gl-accounts/GlAccounts.cjsx +31 -0
  92. data/client/skr/screens/gl-accounts/index.js +5 -0
  93. data/client/skr/screens/gl-transactions/GlTransactions.cjsx +46 -0
  94. data/client/skr/screens/gl-transactions/index.js +1 -0
  95. data/client/skr/screens/gl-transactions/index.scss +12 -0
  96. data/client/skr/screens/invoice/Invoice.cjsx +92 -0
  97. data/client/skr/screens/invoice/Payment.cjsx +28 -0
  98. data/client/skr/screens/invoice/index.js +5 -0
  99. data/client/skr/screens/locations/Locations.cjsx +31 -0
  100. data/client/skr/screens/locations/index.js +5 -0
  101. data/client/skr/screens/payment-category/PaymentCategory.cjsx +28 -0
  102. data/client/skr/screens/payment-category/index.js +5 -0
  103. data/client/skr/screens/payment-category/index.scss +9 -0
  104. data/client/skr/screens/payment-terms/PaymentTerms.cjsx +28 -0
  105. data/client/skr/screens/payment-terms/index.js +5 -0
  106. data/client/skr/screens/payment-terms/index.scss +9 -0
  107. data/client/skr/screens/payments/Payments.cjsx +59 -0
  108. data/client/skr/screens/payments/index.js +5 -0
  109. data/client/skr/screens/sales-order/SalesOrder.cjsx +68 -0
  110. data/client/skr/screens/sales-order/index.js +1 -5
  111. data/client/skr/screens/sku-maint/SkuMaint.cjsx +53 -0
  112. data/client/skr/screens/sku-maint/SkuUomList.cjsx +209 -0
  113. data/client/skr/screens/sku-maint/index.js +2 -5
  114. data/client/skr/screens/sku-maint/index.scss +37 -6
  115. data/client/skr/screens/time-invoicing/TimeInvoicing.cjsx +156 -0
  116. data/client/skr/screens/time-invoicing/index.js +5 -0
  117. data/client/skr/screens/time-invoicing/index.scss +7 -0
  118. data/client/skr/screens/time-tracking/EditEntry.cjsx +54 -0
  119. data/client/skr/screens/time-tracking/Entries.coffee +132 -0
  120. data/client/skr/screens/time-tracking/Header.cjsx +71 -0
  121. data/client/skr/screens/time-tracking/Popover.cjsx +70 -0
  122. data/client/skr/screens/time-tracking/PopoverMiniControls.cjsx +25 -0
  123. data/client/skr/screens/time-tracking/TimeTracking.cjsx +63 -0
  124. data/client/skr/screens/time-tracking/WeekSummary.cjsx +16 -0
  125. data/client/skr/screens/time-tracking/index.js +1 -0
  126. data/client/skr/screens/time-tracking/index.scss +126 -0
  127. data/client/skr/screens/vendor-maint/VendorMaint.cjsx +45 -0
  128. data/client/skr/screens/vendor-maint/index.js +1 -5
  129. data/client/skr/styles.scss +1 -13
  130. data/config/puma.rb +4 -0
  131. data/config/routes.rb +21 -9
  132. data/config/screens.rb +147 -32
  133. data/db/migrate/20140202194700_create_skr_gl_transaction_details.rb +35 -0
  134. data/db/migrate/20140220031800_create_skr_locations.rb +2 -0
  135. data/db/migrate/20140220190836_create_skr_vendors.rb +1 -0
  136. data/db/migrate/20140220203029_create_skr_customers.rb +3 -2
  137. data/db/migrate/20140224034759_create_skr_skus.rb +2 -3
  138. data/db/migrate/20140322223912_create_skr_sales_orders.rb +4 -3
  139. data/db/migrate/20140322223920_create_skr_so_lines.rb +4 -5
  140. data/db/migrate/20140323001446_create_so_details_view.rb +7 -5
  141. data/db/migrate/20140327202209_create_skr_pt_lines.rb +2 -2
  142. data/db/migrate/20140327214000_create_customer_project.rb +16 -0
  143. data/db/migrate/20140327223002_create_time_entries.rb +17 -0
  144. data/db/migrate/20140327224000_create_skr_invoices.rb +5 -1
  145. data/db/migrate/20140327224002_create_skr_inv_lines.rb +3 -1
  146. data/db/migrate/20140422024010_create_skr_inv_details_view.rb +1 -1
  147. data/db/migrate/20151121211323_create_customer_project_details_views.rb +31 -0
  148. data/db/migrate/20160216142845_create_gl_account_balances_view.rb +20 -0
  149. data/db/migrate/20160229002044_create_bank_accounts.rb +18 -0
  150. data/db/migrate/20160229041711_create_payments.rb +33 -0
  151. data/db/migrate/20160307022705_create_create_combined_uom_views.rb +18 -0
  152. data/db/schema.sql +639 -140
  153. data/db/seed/chart_of_accounts.yml +8 -6
  154. data/db/seed/payment_categories.yml +12 -0
  155. data/db/seed/skus.yml +32 -0
  156. data/db/seed.rb +21 -0
  157. data/lib/skr/access_roles.rb +19 -6
  158. data/lib/skr/concerns/has_sku_loc_lines.rb +0 -16
  159. data/lib/skr/concerns/inv_extensions.rb +24 -0
  160. data/lib/skr/concerns/is_sku_loc_line.rb +3 -4
  161. data/lib/skr/concerns/so_extensions.rb +8 -0
  162. data/lib/skr/concerns/visible_id_identifier.rb +1 -1
  163. data/lib/skr/db/migration_helpers.rb +2 -4
  164. data/lib/skr/extension.rb +11 -4
  165. data/lib/skr/handlers/fresh_books_import.rb +20 -0
  166. data/lib/skr/handlers/invoice_from_time_entries.rb +49 -0
  167. data/lib/skr/jobs/fresh_books/base.rb +54 -0
  168. data/lib/skr/jobs/fresh_books/import.rb +151 -0
  169. data/lib/skr/jobs/fresh_books/retrieve.rb +62 -0
  170. data/lib/skr/model.rb +5 -0
  171. data/lib/skr/models/address.rb +1 -1
  172. data/lib/skr/models/bank_account.rb +13 -0
  173. data/lib/skr/models/business_entity.rb +3 -0
  174. data/lib/skr/models/customer.rb +1 -1
  175. data/lib/skr/models/customer_project.rb +22 -0
  176. data/lib/skr/models/gl_account.rb +13 -3
  177. data/lib/skr/models/gl_period.rb +6 -0
  178. data/lib/skr/models/gl_posting.rb +7 -4
  179. data/lib/skr/models/gl_transaction.rb +19 -33
  180. data/lib/skr/models/ia_line.rb +1 -1
  181. data/lib/skr/models/inv_line.rb +33 -15
  182. data/lib/skr/models/invoice.rb +47 -20
  183. data/lib/skr/models/location.rb +7 -1
  184. data/lib/skr/models/payment.rb +49 -0
  185. data/lib/skr/models/payment_category.rb +11 -0
  186. data/lib/skr/models/payment_term.rb +1 -1
  187. data/lib/skr/models/por_line.rb +1 -1
  188. data/lib/skr/models/pt_line.rb +1 -1
  189. data/lib/skr/models/sales_order.rb +9 -9
  190. data/lib/skr/models/sku_loc.rb +1 -1
  191. data/lib/skr/models/sku_tran.rb +6 -11
  192. data/lib/skr/models/so_line.rb +22 -12
  193. data/lib/skr/models/time_entry.rb +36 -0
  194. data/lib/skr/models/uom.rb +11 -10
  195. data/lib/skr/models/user_proxy.rb +1 -1
  196. data/lib/skr/number.rb +25 -0
  197. data/lib/skr/print/form.rb +46 -0
  198. data/lib/skr/print/template.rb +48 -0
  199. data/lib/skr/print.rb +11 -0
  200. data/lib/skr/string.rb +11 -0
  201. data/lib/skr/version.rb +1 -1
  202. data/lib/skr.rb +9 -0
  203. data/spec/fixtures/skr/address.yml +164 -4
  204. data/spec/fixtures/skr/bank_account.yml +8 -0
  205. data/spec/fixtures/skr/customer.yml +30 -4
  206. data/spec/fixtures/skr/customer_project.yml +20 -0
  207. data/spec/fixtures/skr/gl_account.yml +19 -1
  208. data/spec/fixtures/skr/gl_period.yml +3 -0
  209. data/spec/fixtures/skr/gl_posting.yml +22 -1
  210. data/spec/fixtures/skr/gl_transaction.yml +4 -1
  211. data/spec/fixtures/skr/inv_line.yml +35 -0
  212. data/spec/fixtures/skr/invoice.yml +13 -2
  213. data/spec/fixtures/skr/location.yml +5 -0
  214. data/spec/fixtures/skr/payment.yml +10 -0
  215. data/spec/fixtures/skr/payment_category.yml +8 -0
  216. data/spec/fixtures/skr/payment_term.yml +37 -3
  217. data/spec/fixtures/skr/sales_order.yml +12 -1
  218. data/spec/fixtures/skr/sku.yml +78 -1
  219. data/spec/fixtures/skr/sku_loc.yml +42 -1
  220. data/spec/fixtures/skr/sku_tran.yml +24 -1
  221. data/spec/fixtures/skr/so_line.yml +32 -0
  222. data/spec/fixtures/skr/time_entry.yml +82 -0
  223. data/spec/fixtures/skr/uom.yml +52 -1
  224. data/spec/fixtures/skr/vendor.yml +28 -1
  225. data/spec/fixtures/stockor.png +0 -0
  226. data/spec/server/bank_account_spec.rb +10 -0
  227. data/spec/server/customer_project_spec.rb +10 -0
  228. data/spec/server/handlers/invoice_from_time_entries_spec.rb +49 -0
  229. data/spec/server/jobs/fresh_books/import_spec.rb +69 -0
  230. data/spec/server/jobs/fresh_books/retrieve_spec.rb +37 -0
  231. data/spec/server/models/address_spec.rb +4 -3
  232. data/spec/server/models/gl_transaction_spec.rb +3 -3
  233. data/spec/server/models/inv_line_spec.rb +83 -3
  234. data/spec/server/models/invoice_spec.rb +32 -3
  235. data/spec/server/models/payment_spec.rb +40 -0
  236. data/spec/server/models/sales_order_spec.rb +1 -1
  237. data/spec/server/models/so_line_spec.rb +3 -3
  238. data/spec/server/models/spec_helper_spec.rb +1 -1
  239. data/spec/server/payment_spec.rb +10 -0
  240. data/spec/server/print/form_spec.rb +47 -0
  241. data/spec/server/print/template_spec.rb +36 -0
  242. data/spec/server/spec_helper.rb +14 -1
  243. data/spec/server/time_entry_spec.rb +10 -0
  244. data/spec/skr/components/SkuLinesSpec.coffee +61 -0
  245. data/spec/skr/models/BankAccountSpec.coffee +5 -0
  246. data/spec/skr/models/CustomerProjectSpec.coffee +5 -0
  247. data/spec/skr/models/CustomerSpec.coffee +2 -2
  248. data/spec/skr/models/PaymentSpec.coffee +5 -0
  249. data/spec/skr/models/SalesOrderSpec.coffee +21 -5
  250. data/spec/skr/models/SoLineSpec.coffee +7 -2
  251. data/spec/skr/models/TimeEntrySpec.coffee +5 -0
  252. data/spec/skr/screens/bank-maint/BankMaintSpec.coffee +5 -0
  253. data/spec/skr/screens/customer-projects/CustomerProjectsSpec.coffee +5 -0
  254. data/spec/skr/screens/fresh-books-import/FreshBooksImportSpec.coffee +1 -0
  255. data/spec/skr/screens/gl-accounts/GlAccountsSpec.coffee +5 -0
  256. data/spec/skr/screens/invoice/InvoiceSpec.coffee +5 -0
  257. data/spec/skr/screens/locations/LocationsSpec.coffee +5 -0
  258. data/spec/skr/screens/payment-category/PaymentCategorySpec.coffee +5 -0
  259. data/spec/skr/screens/payment-terms/PaymentTermsSpec.coffee +5 -0
  260. data/spec/skr/screens/payments/PaymentsSpec.coffee +5 -0
  261. data/spec/skr/screens/time-invoicing/TimeInvoicingSpec.coffee +5 -0
  262. data/spec/skr/screens/time-tracking/TimeTrackingSpec.coffee +14 -0
  263. data/spec/vcr/freshbooks.yml +698 -0
  264. data/stockor.gemspec +5 -1
  265. data/templates/print/fonts/GnuMICR.otf +0 -0
  266. data/templates/print/layout.tex.erb +39 -0
  267. data/templates/print/packages/booktabs.sty +182 -0
  268. data/templates/print/packages/fancybox.sty +966 -0
  269. data/templates/print/packages/fancyhdr.sty +485 -0
  270. data/templates/print/packages/graphbox.sty +129 -0
  271. data/templates/print/packages/lastpage.sty +283 -0
  272. data/templates/print/packages/lastpage209.sty +70 -0
  273. data/templates/print/packages/marginnote.sty +412 -0
  274. data/templates/print/packages/multirow.sty +159 -0
  275. data/templates/print/packages/rotating.sty +282 -0
  276. data/templates/print/packages/tabu.sty +2557 -0
  277. data/templates/print/packages/textpos.sty +361 -0
  278. data/templates/print/packages/varwidth.sty +318 -0
  279. data/templates/print/partials/address.tex.erb +6 -0
  280. data/templates/print/partials/bill_to_ship_to.tex.erb +13 -0
  281. data/templates/print/partials/header.tex.erb +13 -0
  282. data/templates/print/partials/invoice_paid_state.tex.erb +2 -0
  283. data/templates/print/partials/old/inv_lines_grouping.tex.erb +8 -0
  284. data/templates/print/partials/old/labor_lines_footer.tex.erb +18 -0
  285. data/templates/print/partials/skus_table.tex.erb +32 -0
  286. data/templates/print/partials/skus_table_col_hdr.tex.erb +8 -0
  287. data/templates/print/partials/skus_table_footer.tex.erb +5 -0
  288. data/templates/print/partials/skus_table_group_footer.tex.erb +4 -0
  289. data/templates/print/partials/skus_table_invoice_footer.tex.erb +25 -0
  290. data/templates/print/partials/skus_table_invoice_info_line.tex.erb +16 -0
  291. data/templates/print/partials/skus_table_labor_col_hdr.tex.erb +8 -0
  292. data/templates/print/partials/skus_table_labor_group_footer.tex.erb +7 -0
  293. data/templates/print/partials/skus_table_labor_line.tex.erb +11 -0
  294. data/templates/print/partials/skus_table_line.tex.erb +6 -0
  295. data/templates/print/partials/skus_table_other_charge_lines.tex.erb +7 -0
  296. data/templates/print/partials/so_info_line.tex.erb +14 -0
  297. data/templates/print/types/invoice/default.tex.erb +13 -0
  298. data/templates/print/types/invoice/labor.tex.erb +33 -0
  299. data/templates/print/types/payment/default.tex.erb +64 -0
  300. data/templates/print/types/sales-order/default.tex.erb +8 -0
  301. metadata +252 -15
  302. data/client/skr/components/address/address.html +0 -20
  303. data/client/skr/models/mixins/CodeField.coffee +0 -5
  304. data/client/skr/screens/customer-maint/index.scss +0 -11
  305. data/client/skr/screens/customer-maint/layout.html +0 -32
  306. data/client/skr/screens/sales-order/SalesOrder.coffee +0 -30
  307. data/client/skr/screens/sales-order/index.scss +0 -8
  308. data/client/skr/screens/sales-order/layout.html +0 -30
  309. data/client/skr/screens/sku-maint/SkuMaint.coffee +0 -18
  310. data/client/skr/screens/sku-maint/layout.html +0 -16
  311. data/client/skr/screens/vendor-maint/VendorMaint.coffee +0 -28
  312. data/client/skr/screens/vendor-maint/index.scss +0 -8
  313. data/client/skr/screens/vendor-maint/layout.html +0 -32
@@ -15,7 +15,7 @@
15
15
  number: 1115
16
16
  name: Inventory in Transist
17
17
  description: Inventory on a transfer between locations
18
-
18
+
19
19
  -
20
20
  number: 1200
21
21
  name: Accounts Receivable
@@ -25,7 +25,7 @@
25
25
  name: Prepayment Revenue
26
26
  description: Amounts received in advance of providing goods and services but not yet delivered.
27
27
  -
28
- number: 1750
28
+ number: 1750
29
29
  name: Buildings
30
30
  description: Cost to purchase or construct buildings for use by the company.
31
31
  -
@@ -50,7 +50,7 @@
50
50
  -
51
51
  number: 2300
52
52
  name: Wages Payable
53
- description: Amount owed to employees for hours worked but not yet paid.
53
+ description: Amount owed to employees for hours worked but not yet paid.
54
54
 
55
55
  -
56
56
  number: 2400
@@ -62,7 +62,7 @@
62
62
  name: Interest Payable
63
63
  description: Amount owed for interest on Notes Payable.
64
64
 
65
- -
65
+ -
66
66
  number: 2600
67
67
  name: Inventory Receipts Clearing
68
68
  description: Inventory received but vendor invoice not yet confirmed
@@ -131,7 +131,10 @@
131
131
  number: 6420
132
132
  name: Inbound Shipping Expense
133
133
  description: Cost of vendors shipping goods to us
134
-
134
+ -
135
+ number: 6450
136
+ name: Misc Expense
137
+ description: Miscellaneous costs incurred
135
138
  -
136
139
  number: 6500
137
140
  name: Depreciation Expense
@@ -165,4 +168,3 @@
165
168
  number: 8100
166
169
  name: Non operating Expense
167
170
  description: Expense incurred by other means
168
-
@@ -0,0 +1,12 @@
1
+ -
2
+ code: MISCEXP
3
+ name: Miscellaneous Expenses
4
+ gl_account: 6450
5
+ -
6
+ code: UTILITIES
7
+ name: Utilities Expense
8
+ gl_account: 6700
9
+ -
10
+ code: LABOR
11
+ name: Payments for work performed
12
+ gl_account: 6200
data/db/seed/skus.yml ADDED
@@ -0,0 +1,32 @@
1
+ -
2
+ code: MISC
3
+ description: Miscellaneous Item
4
+ default_uom_code: EA
5
+ is_other_charge: false
6
+ does_track_inventory: false
7
+ gl_asset_account: 1110
8
+ can_backorder: false
9
+ -
10
+ code: LABOR
11
+ description: Work performed
12
+ default_uom_code: HOUR
13
+ is_other_charge: false
14
+ does_track_inventory: false
15
+ gl_asset_account: 6200
16
+ can_backorder: false
17
+ -
18
+ code: TAX
19
+ description: Sales Tax
20
+ is_other_charge: true
21
+ does_track_inventory: false
22
+ default_uom_code: EA
23
+ gl_asset_account: 1900
24
+ can_backorder: false
25
+ -
26
+ code: SHIP
27
+ description: Sales Ship
28
+ is_other_charge: true
29
+ does_track_inventory: false
30
+ default_uom_code: EA
31
+ gl_asset_account: 6410
32
+ can_backorder: false
data/db/seed.rb CHANGED
@@ -27,6 +27,27 @@ module Skr
27
27
  YAML::load( seeds_path.join('payment_terms.yml').read ).each do | acct_data |
28
28
  PaymentTerm.where(code: acct_data['code'].to_s).any? || PaymentTerm.create!(acct_data)
29
29
  end
30
+
31
+ YAML::load( seeds_path.join('skus.yml').read ).each do | sku_data |
32
+ unless Sku.where(code: sku_data['code'].to_s).any?
33
+ glasset = GlAccount.where(number: sku_data.delete('gl_asset_account')).first
34
+ sku = Sku.new(sku_data)
35
+ sku.uoms << Uom.new(code: sku_data['default_uom_code'],
36
+ size:1, price: '0.0')
37
+ sku.gl_asset_account = glasset
38
+ sku.save!
39
+ end
40
+ end
41
+
42
+ YAML::load( seeds_path.join('payment_categories.yml').read ).each do | category_data |
43
+ next if Skr::PaymentCategory.exists?(code: category_data['code'])
44
+ Skr::PaymentCategory.create!(
45
+ code: category_data['code'],
46
+ name: category_data['name'],
47
+ gl_account: GlAccount.find_by_number(category_data['gl_account'])
48
+ )
49
+ end
50
+
30
51
  end
31
52
 
32
53
  end
@@ -4,21 +4,27 @@ require_relative "model"
4
4
  module Lanes::Access
5
5
  module Roles
6
6
 
7
-
8
7
  # re-open the exising Support role
9
8
  class Support
10
- grant Skr::Customer,
11
- Skr::Sku,
12
- Skr::SalesOrder
9
+ read Skr::Invoice,
10
+ Skr::Customer,
11
+ Skr::Sku
13
12
 
13
+ grant Skr::SalesOrder,
14
+ Skr::TimeEntry
14
15
  end
15
16
 
16
-
17
17
  class Accounting < Lanes::Access::Role
18
18
  grant Skr::Customer,
19
19
  Skr::PaymentTerm,
20
+ Skr::CustomerProject,
20
21
  Skr::Sku,
21
- Skr::SalesOrder
22
+ Skr::SalesOrder,
23
+ Skr::TimeEntry,
24
+ Skr::GlTransaction,
25
+ Skr::BankAccount,
26
+ Skr::Payment
27
+
22
28
  lock_writes Skr::Customer, :terms
23
29
  lock Skr::Sku, :gl_asset_account
24
30
  lock Skr::Customer, :gl_receivables_account
@@ -31,6 +37,13 @@ module Lanes::Access
31
37
  Skr::SalesOrder
32
38
  end
33
39
 
40
+ class Workforce < Lanes::Access::Role
41
+ read Skr::Customer,
42
+ Skr::Sku
43
+ grant Skr::SalesOrder,
44
+ Skr::Invoice,
45
+ Skr::TimeEntry
46
+ end
34
47
  end
35
48
 
36
49
  Role.grant_global_access(Skr::Address)
@@ -7,22 +7,6 @@ module Skr
7
7
 
8
8
  module InstanceMethods
9
9
 
10
- def other_charge_lines
11
- self.lines.select{|l| l.sku.is_other_charge? }
12
- end
13
-
14
- def regular_lines
15
- self.lines.reject{|l| l.sku.is_other_charge? }
16
- end
17
-
18
- def regular_lines_total
19
- self.regular_lines.sum{|l|l.extended_price}
20
- end
21
-
22
- def subtotal
23
- self.regular_lines.inject(0){|sum,line| sum + line.extended_price }
24
- end
25
-
26
10
  def total
27
11
  if total = self.read_attribute('total')
28
12
  BigDecimal.new(total)
@@ -5,6 +5,22 @@ module Skr
5
5
 
6
6
  module Lines
7
7
 
8
+ def other_charge
9
+ select{|l| l.sku.is_other_charge? }
10
+ end
11
+
12
+ def regular
13
+ reject{|l| l.sku.is_other_charge? }
14
+ end
15
+
16
+ def product
17
+ reject{|l| l.time_entry }
18
+ end
19
+
20
+ def time_entry
21
+ select{|l| l.time_entry }
22
+ end
23
+
8
24
  def from_pick_ticket!
9
25
  proxy_association.owner.pick_ticket.lines.each do | line |
10
26
  build({ pt_line: line, qty: line.qty_to_ship })
@@ -17,6 +33,14 @@ module Skr
17
33
  end
18
34
  end
19
35
 
36
+ def ea_qty
37
+ if proxy_association.loaded?
38
+ inject(0){ | sum, il | sum+(il.qty*il.uom_size) }
39
+ else
40
+ sum('qty*uom_size')
41
+ end
42
+ end
43
+
20
44
  end
21
45
 
22
46
  end
@@ -48,15 +48,14 @@ module Skr
48
48
 
49
49
  if parent
50
50
  before_create do
51
- self.position ||= ( self.send( parent ).lines.maximum(:position) || 0 ) + 1
51
+ self.position ||= self.send( parent ).lines.max_by{|l|
52
+ l.position || 0
53
+ }.position.to_i + 1
52
54
  end
53
55
  end
54
-
55
56
  export_methods :extended_price, :optional=>false
56
-
57
57
  end
58
58
 
59
-
60
59
  end
61
60
 
62
61
  end
@@ -5,6 +5,14 @@ module Skr
5
5
 
6
6
  module Lines
7
7
 
8
+ def other_charge
9
+ select{|l| l.sku.is_other_charge? }
10
+ end
11
+
12
+ def regular
13
+ reject{|l| l.sku.is_other_charge? }
14
+ end
15
+
8
16
  def set_ship_qty
9
17
  each{|l| l.qty_to_ship = l.qty }
10
18
  end
@@ -11,7 +11,7 @@ module Skr
11
11
  # setup the visible id to the next available #{Skr::SequentialId}
12
12
  # @return [Integer] the assigned ID
13
13
  def assign_visible_id!
14
- self.visible_id = Skr::SequentialId.next_for( self.class )
14
+ self.visible_id ||= Skr::SequentialId.next_for( self.class )
15
15
  end
16
16
  end
17
17
 
@@ -12,10 +12,8 @@ module Skr
12
12
  end
13
13
 
14
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
- }
15
+ column( :visible_id, :string, :null=>false )
16
+ skr_extra_indexes['visible_id'] = {}
19
17
  end
20
18
 
21
19
  def skr_state
data/lib/skr/extension.rb CHANGED
@@ -1,14 +1,16 @@
1
1
  require 'lanes/access/extension'
2
-
3
2
  module Skr
4
3
 
5
4
  class Extension < Lanes::Extensions::Definition
6
5
 
7
6
  identifier "skr"
8
-
7
+ title "Stockor"
9
8
  root_path Pathname.new(__FILE__).dirname.join("..","..").expand_path
10
9
 
11
- components 'record-finder', 'select-field'
10
+ components 'record-finder', 'select-field', 'calendar'
11
+ client_js_aliases({
12
+ 'SC' => 'window.Lanes.Skr.Components'
13
+ })
12
14
 
13
15
  def client_bootstrap_data(view)
14
16
  gl_accounts = Skr::GlAccount.all.as_json
@@ -17,10 +19,15 @@ module Skr
17
19
  account = gl_accounts.detect{|gla|gla['number'] == number}
18
20
  [code, account ? account['id'] : 0]
19
21
  }],
20
- gl_accounts: gl_accounts
22
+ gl_accounts: gl_accounts,
23
+ payment_terms: Skr::PaymentTerm.all.as_json,
24
+ locations: Skr::Location.all.as_json,
25
+ templates: Skr::Print::Template.as_json
21
26
  }
22
27
  end
23
28
 
24
29
  end
25
30
 
26
31
  end
32
+
33
+ require 'skr'
@@ -0,0 +1,20 @@
1
+ require 'ruby-freshbooks'
2
+
3
+ module Skr::Handlers
4
+
5
+ class FreshBooksImport
6
+
7
+ def self.handler
8
+ lambda do
9
+ resp = if data['stage'] == 'fetch'
10
+ Skr::Jobs::FreshBooks::Retrieve.from_request(data)
11
+ else
12
+ Skr::Jobs::FreshBooks::Import.from_request(data)
13
+ end
14
+ json_reply resp
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,49 @@
1
+ module Skr
2
+ module Handlers
3
+ class InvoiceFromTimeEntries
4
+
5
+ def initialize(project_id, entry_ids, options = {})
6
+ @project = CustomerProject.find(project_id)
7
+ @entry_ids = entry_ids
8
+
9
+ @location = Location.default # should be set on project maybe?
10
+ @sku_loc = @project.sku.sku_locs.find_by(location: @location)
11
+ @options = options
12
+ end
13
+
14
+ def build_invoice
15
+ invoice = Invoice.new(
16
+ customer_project: @project,
17
+ customer: @project.customer,
18
+ po_num: @options['po_num'] || @project.po_num,
19
+ notes: @options['notes']
20
+ )
21
+ @entry_ids.each do | entry_id |
22
+ entry = TimeEntry.find(entry_id)
23
+ invoice.lines.build(
24
+ time_entry: entry,
25
+ sku_loc: @sku_loc,
26
+ price: @project.rates['hourly'],
27
+ description: entry.description,
28
+ qty: ((entry.end_at - entry.start_at) / 1.hour)
29
+ )
30
+ end
31
+ invoice
32
+ end
33
+
34
+
35
+ def self.handler
36
+ lambda do
37
+ wrap_reply do
38
+ builder = InvoiceFromTimeEntries.new(
39
+ data['customer_project_id'], data['time_entry_ids'], data
40
+ )
41
+ invoice = builder.build_invoice
42
+ std_api_reply :create, { invoice: invoice }, success: invoice.save
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,54 @@
1
+ require 'ruby-freshbooks'
2
+
3
+ module Skr
4
+ module Jobs
5
+ module FreshBooks
6
+
7
+ class Base < Lanes::Job
8
+ STEPS = %w{staff clients projects time_entries invoices}
9
+
10
+ attr_reader :fb
11
+ def resp_data_for_req_type(type, resp)
12
+ if type == 'staff'
13
+ data = resp['staff_members']
14
+ data[type]=data.delete('member')
15
+ data
16
+ else
17
+ resp[type.pluralize]
18
+ end
19
+ end
20
+
21
+ def fetch_each(reqtype)
22
+ Enumerator.new { | enum |
23
+ page = last_page = 1
24
+ while page <= last_page
25
+ resp = fb.__send__(reqtype).list(per_page: 25, page: page)
26
+ data = resp_data_for_req_type(reqtype, resp)
27
+ last_page = data['pages'].to_i
28
+ page += 1
29
+ Array.wrap(data[reqtype]).each{|record|
30
+ enum.yield record
31
+ }
32
+ end
33
+ }
34
+ end
35
+
36
+
37
+ def process_each_type(account, token)
38
+ @fb = ::FreshBooks::Client.new("#{account}.freshbooks.com", token)
39
+ output = {}
40
+ @ignored_ids ||= {}
41
+ STEPS.each_with_index do | step, index |
42
+ type = step.singularize
43
+ output[step] = fetch_each( type ).map { | record |
44
+ send("process_#{type}", record)
45
+ }.reject(&:nil?)
46
+ yield output, index
47
+ end
48
+ output
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,151 @@
1
+ require_relative 'base'
2
+
3
+ module Skr::Jobs::FreshBooks
4
+
5
+ class Import < Base
6
+ def to_name(r)
7
+ [r['first_name'], r['last_name']].compact.join(' ')
8
+ end
9
+
10
+ def customer_for_fb_id(id)
11
+ code = @customer_codes[id]
12
+ if code
13
+ Skr::Customer.find_by_code(code)
14
+ else
15
+ Skr::Customer.where("options ->>'freshbooks_id' = ?", id).first
16
+ end
17
+ end
18
+
19
+
20
+ def is_ignored?(type, id)
21
+ @ignored_ids[type] && @ignored_ids[type].include?(id)
22
+ end
23
+
24
+ def get_user(staff_id)
25
+ if @user_mappings[staff_id]
26
+ Lanes::User.find(@user_mappings[staff_id])
27
+ else
28
+ Lanes::User.where("options ->>'freshbooks_id' = ?", staff_id).first
29
+ end
30
+ end
31
+
32
+ def process_client(r)
33
+ return nil if is_ignored?('clients', r['client_id'])
34
+ code = @customer_codes[ r['client_id'] ]
35
+ return if code and Skr::Customer.find_by_code(code)
36
+ Skr::Customer.create(
37
+ code: code,
38
+ name: r['organization'], options: { freshbooks_id: r['client_id'] },
39
+ notes: r['notes'], credit_limit: r['credit'],
40
+ billing_address_attributes: {
41
+ name: to_name(r),
42
+ email: r['email'], phone: r['work_phone'] || r['mobile'] || r['home_phone'],
43
+ line1: r['p_street1'], line2: r['p_street2'], city: r['p_city'],
44
+ state: r['p_state'], postal_code: r['p_code']
45
+ },
46
+ shipping_address_attributes: {
47
+ name: to_name(r),
48
+ email: r['email'], phone: r['mobile'] || r['work_phone'] || r['home_phone'],
49
+ line1: r['s_street1'], line2: r['s_street2'], city: r['s_city'],
50
+ state: r['s_state'], postal_code: r['s_code']
51
+ }
52
+ )
53
+ end
54
+
55
+ def process_staff(r)
56
+ return nil if is_ignored?('staff', r['staff_id']) or @user_mappings[r['staff_id']]
57
+ Lanes::User.create(
58
+ login: r['username'],
59
+ name: to_name(r),
60
+ password: 'password',
61
+ email: r['email'],
62
+ options: { freshbooks_id: r['staff_id'] },
63
+ role_names: ['workforce']
64
+ )
65
+ end
66
+
67
+ def process_project(r)
68
+ return nil if is_ignored?('projects', r['project_id'])
69
+ Skr::CustomerProject.create(
70
+ name: r['name'], description: r['description'],
71
+ sku: Skr::Sku.find_by(code: 'LABOR'),
72
+ customer: customer_for_fb_id(r['client_id']),
73
+ options: { freshbooks_id: r['project_id'] },
74
+ rates: {"hourly": r['rate']},
75
+ )
76
+ end
77
+
78
+ def process_time_entry(r)
79
+ return nil if is_ignored?('time_entries', r['time_entry_id'])
80
+ Skr::TimeEntry.create(
81
+ is_invoiced: r['billed'] == '1',
82
+ options: { freshbooks_id: r['time_entry_id'] },
83
+ customer_project: Skr::CustomerProject.where("options ->>'freshbooks_id' = ?", r['project_id']).first,
84
+ description: r['notes'],
85
+ start_at: DateTime.parse(r['date']) + 8.hours,
86
+ end_at: DateTime.parse(r['date']) + 8.hours + r['hours'].to_i.hours,
87
+ lanes_user: get_user(r['staff_id'])
88
+ )
89
+ end
90
+
91
+ def process_invoice(r)
92
+ return nil if is_ignored?('invoices', r['invoice_id'])
93
+
94
+ inv = Skr::Invoice.create(
95
+ visible_id: r['number'].sub(/^0+/,''),
96
+ options: { freshbooks_id: r['invoice_id'] },
97
+ customer: customer_for_fb_id(r['client_id']),
98
+ location: Skr::Location.default,
99
+ po_num: r['po_number'], notes: r['notes'],
100
+ invoice_date: DateTime.parse(r['date']),
101
+ terms: Skr::PaymentTerm.find_by(days: r['terms'].to_s[/\d+/, 0] || 30),
102
+ billing_address_attributes: {
103
+ name: to_name(r),
104
+ email: r['email'], phone: r['mobile'] || r['work_phone'] || r['home_phone'],
105
+ line1: r['p_street1'], line2: r['p_street2'], city: r['p_city'],
106
+ state: r['p_state'], postal_code: r['p_code']
107
+ },
108
+ lines_attributes: Array.wrap(r['lines']['line']).map { | l |
109
+ is_time = 'Time' == l['type']
110
+ if l['name'] or l['description'] # blank name == blank line
111
+ sku = Skr::Sku.find_by(code: is_time ? 'LABOR' : 'MISC')
112
+ invl = {
113
+ sku_loc: sku.sku_locs.default,
114
+ price: l['unit_cost'],
115
+ description: l['description'],
116
+ qty: l['quantity']
117
+ }
118
+ if l.has_key?('time_entries')
119
+ invl['time_entry'] =
120
+ Skr::TimeEntry.find_by("options ->>'freshbooks_id' = ?",
121
+ l['time_entries']['time_entry']['time_entry_id'])
122
+ end
123
+ invl
124
+ end
125
+ }.compact
126
+ )
127
+ if r['paid'].to_f > 0
128
+ inv.update_attributes(amount_paid: r['paid'])
129
+ end
130
+ inv
131
+ end
132
+
133
+ def perform(data)
134
+ @ignored_ids = data['ignored_ids'] || {}
135
+ @user_mappings = data['user_mappings'] || {}
136
+ @customer_codes = data['customer_codes'] || {}
137
+ Skr::Invoice.transaction do
138
+ process_each_type(data['domain'], data['api_key']) do | output, index |
139
+ save_progress(output, (index+1).to_f / STEPS.length)
140
+ end
141
+ end
142
+ self
143
+ end
144
+
145
+ def self.from_request(req)
146
+ Lanes::Job.api_status_message self.perform_later(req)
147
+ end
148
+
149
+ end
150
+
151
+ end
@@ -0,0 +1,62 @@
1
+ require_relative 'base'
2
+
3
+ module Skr::Jobs::FreshBooks
4
+
5
+ class Retrieve < Base
6
+
7
+ def process_client(rec)
8
+ rec.slice(*%w{ client_id organization first_name last_name})
9
+ end
10
+
11
+ def process_project(rec)
12
+ rec.slice(*%w{project_id name description})
13
+ end
14
+
15
+ def process_invoice(rec)
16
+ rec.slice(*%w{invoice_id client_id number amount po_number notes status})
17
+ end
18
+
19
+ def process_time_entry(rec)
20
+ if rec['billed'] == '1'
21
+ nil
22
+ else
23
+ rec.slice(*%w{ time_entry_id staff_id project_id hours date notes})
24
+ end
25
+ end
26
+
27
+ def process_staff(r)
28
+ r.slice(*%w{staff_id username first_name last_name email})
29
+ end
30
+
31
+ def perform(account, token)
32
+ process_each_type(account, token) do | output, index |
33
+ save_progress(output, (index+1).to_f / STEPS.length)
34
+ end
35
+ self
36
+ end
37
+
38
+ def self.from_request(req)
39
+ fb = ::FreshBooks::Client.new("#{req['domain']}.freshbooks.com", req['api_key'])
40
+ errors = nil
41
+ begin
42
+ # make a test api call to validate authentication
43
+ resp = fb.client.list(per_page: 1)
44
+ errors = {access: resp['error']} if resp['error']
45
+ rescue SocketError
46
+ errors = {network: 'Unable to resolve account'}
47
+ end
48
+ if errors
49
+ return {success: false, data: {}, errors: errors}
50
+ else
51
+ job = self.perform_later(req['domain'], req['api_key'])
52
+ return {
53
+ success: true, message: 'Import Validation Started', data: {
54
+ job: Lanes::Job.status_for_id(job.job_id)
55
+ }
56
+ }
57
+ end
58
+
59
+ end
60
+ end
61
+
62
+ end
data/lib/skr/model.rb CHANGED
@@ -49,4 +49,9 @@ module Skr
49
49
  autoload :Vendor, "skr/models/vendor"
50
50
  autoload :VoLine, "skr/models/vo_line"
51
51
  autoload :Voucher, "skr/models/voucher"
52
+ autoload :TimeEntry, "skr/models/time_entry"
53
+ autoload :CustomerProject, "skr/models/customer_project"
54
+ autoload :BankAccount, "skr/models/bank_account"
55
+ autoload :PaymentCategory, "skr/models/payment_category"
56
+ autoload :Payment, "skr/models/payment"
52
57
  end