brisk-bills 0.6.0

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.
Files changed (524) hide show
  1. data/CHANGELOG +8 -0
  2. data/COPYING +674 -0
  3. data/COPYING.LESSER +165 -0
  4. data/INSTALL +5 -0
  5. data/README +22 -0
  6. data/Rakefile +11 -0
  7. data/TODO.txt +112 -0
  8. data/app/controllers/admin/activities_controller.rb +151 -0
  9. data/app/controllers/admin/activities_controller/adjustment.rb +16 -0
  10. data/app/controllers/admin/activities_controller/labor.rb +119 -0
  11. data/app/controllers/admin/activities_controller/labor/slimtimer.rb +28 -0
  12. data/app/controllers/admin/activities_controller/material.rb +20 -0
  13. data/app/controllers/admin/activities_controller/proposal.rb +15 -0
  14. data/app/controllers/admin/activities_with_prices_controller.rb +68 -0
  15. data/app/controllers/admin/adjustments_controller.rb +22 -0
  16. data/app/controllers/admin/client_accounting_controller.rb +49 -0
  17. data/app/controllers/admin/client_financial_transactions_controller.rb +24 -0
  18. data/app/controllers/admin/client_representatives_controller.rb +31 -0
  19. data/app/controllers/admin/clients_controller.rb +31 -0
  20. data/app/controllers/admin/employee_client_labor_rate_controller.rb +30 -0
  21. data/app/controllers/admin/employees_controller.rb +32 -0
  22. data/app/controllers/admin/employees_controller/slimtimer.rb +29 -0
  23. data/app/controllers/admin/invoices_controller.rb +165 -0
  24. data/app/controllers/admin/labors_controller.rb +103 -0
  25. data/app/controllers/admin/labors_controller/slimtimer.rb +31 -0
  26. data/app/controllers/admin/materials_controller.rb +24 -0
  27. data/app/controllers/admin/payments_controller.rb +75 -0
  28. data/app/controllers/admin/proposals_controller.rb +22 -0
  29. data/app/controllers/admin/settings_controller.rb +23 -0
  30. data/app/controllers/application_controller.rb +22 -0
  31. data/app/controllers/authentication_controller.rb +131 -0
  32. data/app/helpers/activity_type_model_helper.rb +194 -0
  33. data/app/helpers/admin/activities_helper.rb +110 -0
  34. data/app/helpers/admin/activities_helper/labor_helper.rb +28 -0
  35. data/app/helpers/admin/activities_helper/proposal_helper.rb +11 -0
  36. data/app/helpers/admin/activities_helper/slimtimer_helper.rb +13 -0
  37. data/app/helpers/admin/activities_with_prices_helper.rb +17 -0
  38. data/app/helpers/admin/activity_tax_controller_helper.rb +48 -0
  39. data/app/helpers/admin/activity_tax_field_helper.rb +33 -0
  40. data/app/helpers/admin/activity_type_controller_helper.rb +83 -0
  41. data/app/helpers/admin/activity_type_field_helper.rb +45 -0
  42. data/app/helpers/admin/adjustments_helper.rb +5 -0
  43. data/app/helpers/admin/client_accounting_helper.rb +13 -0
  44. data/app/helpers/admin/client_financial_transactions_helper.rb +9 -0
  45. data/app/helpers/admin/client_representatives_helper.rb +26 -0
  46. data/app/helpers/admin/clients_helper.rb +6 -0
  47. data/app/helpers/admin/employee_client_labor_rate_helper.rb +77 -0
  48. data/app/helpers/admin/employees_helper.rb +9 -0
  49. data/app/helpers/admin/employees_helper/slimtimer_helper.rb +2 -0
  50. data/app/helpers/admin/has_credential_column_helper.rb +60 -0
  51. data/app/helpers/admin/invoices_helper.rb +59 -0
  52. data/app/helpers/admin/is_active_column_helper.rb +46 -0
  53. data/app/helpers/admin/labors_helper.rb +20 -0
  54. data/app/helpers/admin/labors_helper/slimtimer_helper.rb +13 -0
  55. data/app/helpers/admin/materials_helper.rb +5 -0
  56. data/app/helpers/admin/payments_helper.rb +41 -0
  57. data/app/helpers/admin/proposals_helper.rb +3 -0
  58. data/app/helpers/admin/settings_helper.rb +8 -0
  59. data/app/helpers/admin_layout_helper.rb +91 -0
  60. data/app/helpers/application_helper.rb +27 -0
  61. data/app/helpers/authentication_helper.rb +61 -0
  62. data/app/helpers/extensible_object_helper.rb +28 -0
  63. data/app/helpers/has_credential_model_helper.rb +39 -0
  64. data/app/helpers/invoice_pdf_helper.rb +71 -0
  65. data/app/helpers/is_active_model_helper.rb +40 -0
  66. data/app/helpers/money_model_helper.rb +21 -0
  67. data/app/model_views/client_accounting.rb +5 -0
  68. data/app/model_views/client_financial_transaction.rb +15 -0
  69. data/app/model_views/invoices_with_total.rb +36 -0
  70. data/app/models/activity.rb +104 -0
  71. data/app/models/activity/adjustment.rb +34 -0
  72. data/app/models/activity/labor.rb +91 -0
  73. data/app/models/activity/labor/slimtimer.rb +28 -0
  74. data/app/models/activity/material.rb +16 -0
  75. data/app/models/activity/proposal.rb +17 -0
  76. data/app/models/activity_type.rb +2 -0
  77. data/app/models/client.rb +93 -0
  78. data/app/models/client_eventlog.rb +5 -0
  79. data/app/models/client_representative.rb +22 -0
  80. data/app/models/credential.rb +195 -0
  81. data/app/models/employee.rb +49 -0
  82. data/app/models/employee/labor.rb +13 -0
  83. data/app/models/employee/slimtimer.rb +43 -0
  84. data/app/models/employee_client_labor_rate.rb +20 -0
  85. data/app/models/invoice.rb +280 -0
  86. data/app/models/invoice_payment.rb +8 -0
  87. data/app/models/notifier.rb +85 -0
  88. data/app/models/payment.rb +108 -0
  89. data/app/models/payment_method.rb +7 -0
  90. data/app/models/setting.rb +39 -0
  91. data/app/models/slimtimer_task.rb +6 -0
  92. data/app/models/slimtimer_time_entry.rb +69 -0
  93. data/app/views/admin/activities/_adjustment_column.rhtml +23 -0
  94. data/app/views/admin/activities/_form.rhtml +20 -0
  95. data/app/views/admin/activities/_form_attribute.rhtml +12 -0
  96. data/app/views/admin/activities/_labor_column.rhtml +17 -0
  97. data/app/views/admin/activities/_material_column.rhtml +10 -0
  98. data/app/views/admin/activities/_proposal_column.rhtml +11 -0
  99. data/app/views/admin/activities_with_prices/move_to_invoice.rhtml +44 -0
  100. data/app/views/admin/activities_with_prices/move_to_invoice.rjs +9 -0
  101. data/app/views/admin/invoices/download.rpdf +224 -0
  102. data/app/views/authentication/email.rjs +9 -0
  103. data/app/views/authentication/login.rhtml +75 -0
  104. data/app/views/authentication/login.rjs +14 -0
  105. data/app/views/authentication/reset_password_via_token.rhtml +16 -0
  106. data/app/views/authentication/reset_password_via_token.rjs +6 -0
  107. data/app/views/authentication/sign_in_error.rjs +2 -0
  108. data/app/views/layouts/_navigation_tree.rhtml +15 -0
  109. data/app/views/layouts/admin.rhtml +26 -0
  110. data/app/views/layouts/public.rhtml +18 -0
  111. data/app/views/notifier/_email_footer.html.rhtml +12 -0
  112. data/app/views/notifier/_email_footer.plain.rhtml +3 -0
  113. data/app/views/notifier/_email_header.html.rhtml +53 -0
  114. data/app/views/notifier/invoice_available.html.rhtml +5 -0
  115. data/app/views/notifier/invoice_available.plain.rhtml +4 -0
  116. data/app/views/notifier/reset_password_requested.html.rhtml +15 -0
  117. data/app/views/notifier/reset_password_requested.plain.rhtml +9 -0
  118. data/bin/brisk-bills +51 -0
  119. data/config/boot.rb +164 -0
  120. data/config/environment.rb +39 -0
  121. data/config/environments/development.rb +17 -0
  122. data/config/environments/production.rb +18 -0
  123. data/config/environments/test.rb +19 -0
  124. data/config/locale/en.rb +26 -0
  125. data/config/routes.rb +20 -0
  126. data/config/secrets.yml +2 -0
  127. data/db/migrate/001_create_employees.rb +14 -0
  128. data/db/migrate/002_create_employee_slimtimers.rb +14 -0
  129. data/db/migrate/003_create_slimtimer_tasks.rb +21 -0
  130. data/db/migrate/004_create_slimtimer_time_entries.rb +25 -0
  131. data/db/migrate/005_create_clients.rb +12 -0
  132. data/db/migrate/006_create_client_representatives.rb +16 -0
  133. data/db/migrate/007_create_client_eventlogs.rb +13 -0
  134. data/db/migrate/008_create_activities.rb +17 -0
  135. data/db/migrate/009_create_activity_labors.rb +16 -0
  136. data/db/migrate/010_create_employee_client_labor_rates.rb +19 -0
  137. data/db/migrate/011_create_activity_adjustments.rb +16 -0
  138. data/db/migrate/012_create_activity_materials.rb +16 -0
  139. data/db/migrate/013_create_activity_proposals.rb +18 -0
  140. data/db/migrate/014_create_invoices.rb +16 -0
  141. data/db/migrate/015_create_payments.rb +18 -0
  142. data/db/migrate/016_create_payment_methods.rb +18 -0
  143. data/db/migrate/017_create_invoice_payments.rb +14 -0
  144. data/db/migrate/018_create_activity_types.rb +37 -0
  145. data/db/migrate/019_create_settings.rb +45 -0
  146. data/db/migrate/020_is_active_employees_clients.rb +11 -0
  147. data/db/migrate/021_create_views_and_indexes.rb +78 -0
  148. data/db/migrate/022_create_client_financial_transactions.rb +32 -0
  149. data/db/migrate/023_create_credentials_migrate_representatives.rb +133 -0
  150. data/db/migrate/024_updates_for_sales_tax.rb +91 -0
  151. data/db/migrate/025_alter_credentials_for_reset.rb +11 -0
  152. data/db/migrate/026_create_st_settings.rb +17 -0
  153. data/db/migrate/027_create_sessions.rb +16 -0
  154. data/db/migrate/028_money_to_cents.rb +136 -0
  155. data/db/schema.rb +301 -0
  156. data/lib/brisk-bills.rb +17 -0
  157. data/lib/brisk-bills/initializer.rb +100 -0
  158. data/lib/dti-navigation-menu/lib/dti-navigation-menu.rb +27 -0
  159. data/lib/generators/generator_base_extension.rb +16 -0
  160. data/lib/generators/instance/instance_generator.rb +117 -0
  161. data/lib/generators/instance/templates/databases/mysql.yml +47 -0
  162. data/lib/generators/instance/templates/instance_boot.rb +166 -0
  163. data/lib/generators/instance/templates/instance_environment.rb +65 -0
  164. data/lib/generators/instance/templates/instance_generate +18 -0
  165. data/lib/generators/instance/templates/instance_rakefile +3 -0
  166. data/lib/generators/instance/templates/instance_routes.rb +1 -0
  167. data/lib/tasks/create_last_months_invoices.rake +37 -0
  168. data/lib/tasks/credential_tasks.rake +47 -0
  169. data/lib/tasks/first_time_setup.rake +96 -0
  170. data/lib/tasks/instance.rake +27 -0
  171. data/lib/tasks/invoice_as_csv.rake +32 -0
  172. data/lib/tasks/package.rake +120 -0
  173. data/lib/tasks/slimtimer_import.rake +198 -0
  174. data/lib/utilities.rb +18 -0
  175. data/log/.keep +0 -0
  176. data/public/.htaccess +40 -0
  177. data/public/404.html +30 -0
  178. data/public/500.html +30 -0
  179. data/public/blank.html +33 -0
  180. data/public/blank_iframe.html +2 -0
  181. data/public/dispatch.cgi +10 -0
  182. data/public/dispatch.fcgi +24 -0
  183. data/public/dispatch.rb +10 -0
  184. data/public/favicon.ico +0 -0
  185. data/public/images/active_scaffold/DO_NOT_EDIT +2 -0
  186. data/public/images/active_scaffold/default/add.gif +0 -0
  187. data/public/images/active_scaffold/default/arrow_down.gif +0 -0
  188. data/public/images/active_scaffold/default/arrow_up.gif +0 -0
  189. data/public/images/active_scaffold/default/close.gif +0 -0
  190. data/public/images/active_scaffold/default/cross.png +0 -0
  191. data/public/images/active_scaffold/default/indicator-small.gif +0 -0
  192. data/public/images/active_scaffold/default/indicator.gif +0 -0
  193. data/public/images/active_scaffold/default/magnifier.png +0 -0
  194. data/public/images/background.gif +0 -0
  195. data/public/images/brisk-bills-logo.gif +0 -0
  196. data/public/images/calendar_date_select/calendar.gif +0 -0
  197. data/public/images/dti-logo.gif +0 -0
  198. data/public/images/dti-logo.jpg +0 -0
  199. data/public/images/favicon.ico +0 -0
  200. data/public/images/invoice-background.jpg +0 -0
  201. data/public/images/login-form-spinner.gif +0 -0
  202. data/public/images/nav-arrow.gif +0 -0
  203. data/public/images/notice-error.gif +0 -0
  204. data/public/images/notice-info.gif +0 -0
  205. data/public/images/rails.png +0 -0
  206. data/public/javascripts/active_scaffold/DO_NOT_EDIT +2 -0
  207. data/public/javascripts/active_scaffold/default/active_scaffold.js +430 -0
  208. data/public/javascripts/active_scaffold/default/dhtml_history.js +867 -0
  209. data/public/javascripts/active_scaffold/default/form_enhancements.js +114 -0
  210. data/public/javascripts/active_scaffold/default/rico_corner.js +370 -0
  211. data/public/javascripts/application.js +2 -0
  212. data/public/javascripts/briskbills-quick-helpers.js +9 -0
  213. data/public/javascripts/builder.js +136 -0
  214. data/public/javascripts/controls.js +963 -0
  215. data/public/javascripts/dragdrop.js +973 -0
  216. data/public/javascripts/effects.js +1128 -0
  217. data/public/javascripts/modalbox.js +573 -0
  218. data/public/javascripts/prototype.js +4320 -0
  219. data/public/javascripts/scriptaculous.js +58 -0
  220. data/public/javascripts/shadedborder.js +249 -0
  221. data/public/javascripts/slider.js +275 -0
  222. data/public/javascripts/sound.js +55 -0
  223. data/public/javascripts/unittest.js +568 -0
  224. data/public/robots.txt +1 -0
  225. data/public/stylesheets/active_scaffold/DO_NOT_EDIT +2 -0
  226. data/public/stylesheets/active_scaffold/default/stylesheet-ie.css +35 -0
  227. data/public/stylesheets/active_scaffold/default/stylesheet.css +809 -0
  228. data/public/stylesheets/admin/global.css +152 -0
  229. data/public/stylesheets/admin/pages.css +137 -0
  230. data/public/stylesheets/admin/screen.css +3 -0
  231. data/public/stylesheets/admin/structure.css +88 -0
  232. data/public/stylesheets/modalbox.css +95 -0
  233. data/public/stylesheets/public/global.css +36 -0
  234. data/public/stylesheets/public/pages.css +105 -0
  235. data/public/stylesheets/public/screen.css +3 -0
  236. data/public/stylesheets/public/structure.css +0 -0
  237. data/public/stylesheets/spinner.gif +0 -0
  238. data/script/about +3 -0
  239. data/script/breakpointer +3 -0
  240. data/script/console +3 -0
  241. data/script/dbconsole +3 -0
  242. data/script/destroy +3 -0
  243. data/script/generate +3 -0
  244. data/script/performance/benchmarker +3 -0
  245. data/script/performance/profiler +3 -0
  246. data/script/performance/request +3 -0
  247. data/script/plugin +3 -0
  248. data/script/process/inspector +3 -0
  249. data/script/process/reaper +3 -0
  250. data/script/process/spawner +3 -0
  251. data/script/runner +3 -0
  252. data/script/server +3 -0
  253. data/test/fixtures/activities.yml +28 -0
  254. data/test/fixtures/activity_adjustments.yml +7 -0
  255. data/test/fixtures/activity_labors.yml +25 -0
  256. data/test/fixtures/activity_materials.yml +7 -0
  257. data/test/fixtures/activity_proposals.yml +7 -0
  258. data/test/fixtures/activity_types.yml +23 -0
  259. data/test/fixtures/client_eventlogs.yml +5 -0
  260. data/test/fixtures/client_financial_transactions.yml +7 -0
  261. data/test/fixtures/client_representatives.yml +56 -0
  262. data/test/fixtures/clients.yml +172 -0
  263. data/test/fixtures/credentials.yml +34 -0
  264. data/test/fixtures/employee_client_labor_rates.yml +365 -0
  265. data/test/fixtures/employee_slimtimers.yml +19 -0
  266. data/test/fixtures/employees.yml +22 -0
  267. data/test/fixtures/invoice_payments.yml +7 -0
  268. data/test/fixtures/invoices.yml +7 -0
  269. data/test/fixtures/payment_methods.yml +7 -0
  270. data/test/fixtures/payments.yml +7 -0
  271. data/test/fixtures/settings.yml +7 -0
  272. data/test/fixtures/slimtimer_tasks.yml +5 -0
  273. data/test/fixtures/slimtimer_time_entries.yml +5 -0
  274. data/test/functional/admin/activities_controller_test.rb +8 -0
  275. data/test/functional/admin/adjustments_controller_test.rb +8 -0
  276. data/test/functional/admin/client_accounting_controller_test.rb +8 -0
  277. data/test/functional/admin/client_financial_transactions_controller_test.rb +8 -0
  278. data/test/functional/admin/client_representatives_controller_test.rb +8 -0
  279. data/test/functional/admin/clients_controller_test.rb +8 -0
  280. data/test/functional/admin/employee_client_labor_rate_controller_test.rb +8 -0
  281. data/test/functional/admin/employees/slimtimer_controller_test.rb +8 -0
  282. data/test/functional/admin/employees_controller_test.rb +8 -0
  283. data/test/functional/admin/invoices_controller_test.rb +8 -0
  284. data/test/functional/admin/labors_controller_test.rb +8 -0
  285. data/test/functional/admin/materials_controller_test.rb +8 -0
  286. data/test/functional/admin/payments_controller_test.rb +8 -0
  287. data/test/functional/admin/proposals_controller_test.rb +8 -0
  288. data/test/functional/admin/settings_controller_test.rb +8 -0
  289. data/test/functional/authentication_controller_test.rb +8 -0
  290. data/test/test_helper.rb +30 -0
  291. data/test/test_unit_factory_helper.rb +154 -0
  292. data/test/unit/activity/adjustment_test.rb +8 -0
  293. data/test/unit/activity/labor_test.rb +279 -0
  294. data/test/unit/activity/material_test.rb +8 -0
  295. data/test/unit/activity/proposal_test.rb +8 -0
  296. data/test/unit/activity_test.rb +83 -0
  297. data/test/unit/activity_type_test.rb +8 -0
  298. data/test/unit/client_eventlog_test.rb +10 -0
  299. data/test/unit/client_financial_transaction_test.rb +8 -0
  300. data/test/unit/client_representative_test.rb +10 -0
  301. data/test/unit/client_test.rb +24 -0
  302. data/test/unit/credential_test.rb +217 -0
  303. data/test/unit/employee/slimtimer_test.rb +10 -0
  304. data/test/unit/employee_client_labor_rate_test.rb +8 -0
  305. data/test/unit/employee_test.rb +25 -0
  306. data/test/unit/invoice_payment_test.rb +8 -0
  307. data/test/unit/invoice_test.rb +459 -0
  308. data/test/unit/notifier_test.rb +9 -0
  309. data/test/unit/payment_method_test.rb +8 -0
  310. data/test/unit/payment_test.rb +155 -0
  311. data/test/unit/setting_test.rb +8 -0
  312. data/test/unit/slimtimer_task_test.rb +10 -0
  313. data/test/unit/slimtimer_time_entry_test.rb +132 -0
  314. data/vendor/plugins/InlineAttachment-0.3.0-modified/README +79 -0
  315. data/vendor/plugins/InlineAttachment-0.3.0-modified/init.rb +2 -0
  316. data/vendor/plugins/InlineAttachment-0.3.0-modified/lib/inline_attachment.rb +136 -0
  317. data/vendor/plugins/InlineAttachment-0.3.0-modified/lib/tmail_content_id.rb +40 -0
  318. data/vendor/plugins/active_scaffold/CHANGELOG +152 -0
  319. data/vendor/plugins/active_scaffold/MIT-LICENSE +20 -0
  320. data/vendor/plugins/active_scaffold/README +28 -0
  321. data/vendor/plugins/active_scaffold/Rakefile +24 -0
  322. data/vendor/plugins/active_scaffold/environment.rb +17 -0
  323. data/vendor/plugins/active_scaffold/frontends/default/images/add.gif +0 -0
  324. data/vendor/plugins/active_scaffold/frontends/default/images/arrow_down.gif +0 -0
  325. data/vendor/plugins/active_scaffold/frontends/default/images/arrow_up.gif +0 -0
  326. data/vendor/plugins/active_scaffold/frontends/default/images/close.gif +0 -0
  327. data/vendor/plugins/active_scaffold/frontends/default/images/cross.png +0 -0
  328. data/vendor/plugins/active_scaffold/frontends/default/images/indicator-small.gif +0 -0
  329. data/vendor/plugins/active_scaffold/frontends/default/images/indicator.gif +0 -0
  330. data/vendor/plugins/active_scaffold/frontends/default/images/magnifier.png +0 -0
  331. data/vendor/plugins/active_scaffold/frontends/default/javascripts/active_scaffold.js +430 -0
  332. data/vendor/plugins/active_scaffold/frontends/default/javascripts/dhtml_history.js +867 -0
  333. data/vendor/plugins/active_scaffold/frontends/default/javascripts/form_enhancements.js +114 -0
  334. data/vendor/plugins/active_scaffold/frontends/default/javascripts/rico_corner.js +370 -0
  335. data/vendor/plugins/active_scaffold/frontends/default/stylesheets/stylesheet-ie.css +35 -0
  336. data/vendor/plugins/active_scaffold/frontends/default/stylesheets/stylesheet.css +809 -0
  337. data/vendor/plugins/active_scaffold/frontends/default/views/_add_existing_form.html.erb +40 -0
  338. data/vendor/plugins/active_scaffold/frontends/default/views/_create_form.html.erb +49 -0
  339. data/vendor/plugins/active_scaffold/frontends/default/views/_create_form_on_list.html.erb +38 -0
  340. data/vendor/plugins/active_scaffold/frontends/default/views/_field_search.html.erb +45 -0
  341. data/vendor/plugins/active_scaffold/frontends/default/views/_form.html.erb +18 -0
  342. data/vendor/plugins/active_scaffold/frontends/default/views/_form_association.html.erb +20 -0
  343. data/vendor/plugins/active_scaffold/frontends/default/views/_form_association_footer.html.erb +42 -0
  344. data/vendor/plugins/active_scaffold/frontends/default/views/_form_association_header.html.erb +10 -0
  345. data/vendor/plugins/active_scaffold/frontends/default/views/_form_association_record.html.erb +27 -0
  346. data/vendor/plugins/active_scaffold/frontends/default/views/_form_attribute.html.erb +12 -0
  347. data/vendor/plugins/active_scaffold/frontends/default/views/_form_hidden_attribute.html.erb +1 -0
  348. data/vendor/plugins/active_scaffold/frontends/default/views/_form_messages.html.erb +5 -0
  349. data/vendor/plugins/active_scaffold/frontends/default/views/_horizontal_subform.html.erb +21 -0
  350. data/vendor/plugins/active_scaffold/frontends/default/views/_list.html.erb +37 -0
  351. data/vendor/plugins/active_scaffold/frontends/default/views/_list_actions.html.erb +13 -0
  352. data/vendor/plugins/active_scaffold/frontends/default/views/_list_calculations.html.erb +22 -0
  353. data/vendor/plugins/active_scaffold/frontends/default/views/_list_column_headings.html.erb +32 -0
  354. data/vendor/plugins/active_scaffold/frontends/default/views/_list_header.html.erb +14 -0
  355. data/vendor/plugins/active_scaffold/frontends/default/views/_list_inline_adapter.html.erb +9 -0
  356. data/vendor/plugins/active_scaffold/frontends/default/views/_list_pagination_links.html.erb +30 -0
  357. data/vendor/plugins/active_scaffold/frontends/default/views/_list_record.html.erb +35 -0
  358. data/vendor/plugins/active_scaffold/frontends/default/views/_live_search.html.erb +25 -0
  359. data/vendor/plugins/active_scaffold/frontends/default/views/_messages.html.erb +10 -0
  360. data/vendor/plugins/active_scaffold/frontends/default/views/_nested.html.erb +44 -0
  361. data/vendor/plugins/active_scaffold/frontends/default/views/_search.html.erb +21 -0
  362. data/vendor/plugins/active_scaffold/frontends/default/views/_show.html.erb +8 -0
  363. data/vendor/plugins/active_scaffold/frontends/default/views/_show_columns.html.erb +11 -0
  364. data/vendor/plugins/active_scaffold/frontends/default/views/_update_actions.html.erb +9 -0
  365. data/vendor/plugins/active_scaffold/frontends/default/views/_update_form.html.erb +53 -0
  366. data/vendor/plugins/active_scaffold/frontends/default/views/add_existing.js.rjs +16 -0
  367. data/vendor/plugins/active_scaffold/frontends/default/views/add_existing_form.html.erb +5 -0
  368. data/vendor/plugins/active_scaffold/frontends/default/views/create.html.erb +5 -0
  369. data/vendor/plugins/active_scaffold/frontends/default/views/delete.html.erb +13 -0
  370. data/vendor/plugins/active_scaffold/frontends/default/views/destroy.js.rjs +10 -0
  371. data/vendor/plugins/active_scaffold/frontends/default/views/edit_associated.js.rjs +15 -0
  372. data/vendor/plugins/active_scaffold/frontends/default/views/field_search.html.erb +5 -0
  373. data/vendor/plugins/active_scaffold/frontends/default/views/form_messages.js.rjs +1 -0
  374. data/vendor/plugins/active_scaffold/frontends/default/views/form_messages_on_create.js.rjs +2 -0
  375. data/vendor/plugins/active_scaffold/frontends/default/views/form_messages_on_update.js.rjs +2 -0
  376. data/vendor/plugins/active_scaffold/frontends/default/views/list.html.erb +44 -0
  377. data/vendor/plugins/active_scaffold/frontends/default/views/on_create.js.rjs +25 -0
  378. data/vendor/plugins/active_scaffold/frontends/default/views/on_update.js.rjs +11 -0
  379. data/vendor/plugins/active_scaffold/frontends/default/views/search.html.erb +5 -0
  380. data/vendor/plugins/active_scaffold/frontends/default/views/show.html.erb +5 -0
  381. data/vendor/plugins/active_scaffold/frontends/default/views/update.html.erb +8 -0
  382. data/vendor/plugins/active_scaffold/frontends/default/views/update_column.js.rjs +12 -0
  383. data/vendor/plugins/active_scaffold/init.rb +18 -0
  384. data/vendor/plugins/active_scaffold/install.rb +40 -0
  385. data/vendor/plugins/active_scaffold/install_assets.rb +36 -0
  386. data/vendor/plugins/active_scaffold/lib/active_record_permissions.rb +123 -0
  387. data/vendor/plugins/active_scaffold/lib/active_scaffold.rb +144 -0
  388. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/core.rb +102 -0
  389. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/create.rb +120 -0
  390. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/delete.rb +46 -0
  391. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/field_search.rb +46 -0
  392. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/list.rb +77 -0
  393. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/live_search.rb +46 -0
  394. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/nested.rb +215 -0
  395. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/search.rb +40 -0
  396. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/show.rb +34 -0
  397. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/subform.rb +17 -0
  398. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/update.rb +114 -0
  399. data/vendor/plugins/active_scaffold/lib/active_scaffold/attribute_params.rb +185 -0
  400. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/base.rb +37 -0
  401. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/core.rb +189 -0
  402. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/create.rb +43 -0
  403. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/delete.rb +25 -0
  404. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/field_search.rb +53 -0
  405. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/form.rb +49 -0
  406. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/list.rb +134 -0
  407. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/live_search.rb +52 -0
  408. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/nested.rb +31 -0
  409. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/search.rb +52 -0
  410. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/show.rb +39 -0
  411. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/subform.rb +33 -0
  412. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/update.rb +32 -0
  413. data/vendor/plugins/active_scaffold/lib/active_scaffold/configurable.rb +29 -0
  414. data/vendor/plugins/active_scaffold/lib/active_scaffold/constraints.rb +178 -0
  415. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/action_columns.rb +97 -0
  416. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/action_link.rb +136 -0
  417. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/action_links.rb +47 -0
  418. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/actions.rb +45 -0
  419. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/column.rb +297 -0
  420. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/columns.rb +75 -0
  421. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/error_message.rb +24 -0
  422. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/set.rb +62 -0
  423. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/sorting.rb +89 -0
  424. data/vendor/plugins/active_scaffold/lib/active_scaffold/finder.rb +234 -0
  425. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/association_helpers.rb +40 -0
  426. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/controller_helpers.rb +25 -0
  427. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/country_helpers.rb +348 -0
  428. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/form_column_helpers.rb +267 -0
  429. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/id_helpers.rb +125 -0
  430. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/list_column_helpers.rb +178 -0
  431. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/pagination_helpers.rb +37 -0
  432. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/search_column_helpers.rb +172 -0
  433. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/show_column_helpers.rb +46 -0
  434. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/view_helpers.rb +196 -0
  435. data/vendor/plugins/active_scaffold/lib/active_scaffold/locale/en.rb +66 -0
  436. data/vendor/plugins/active_scaffold/lib/active_scaffold/locale/es.yml +64 -0
  437. data/vendor/plugins/active_scaffold/lib/active_scaffold/locale/hu.yml +62 -0
  438. data/vendor/plugins/active_scaffold/lib/bridges/bridge.rb +52 -0
  439. data/vendor/plugins/active_scaffold/lib/bridges/calendar_date_select/bridge.rb +11 -0
  440. data/vendor/plugins/active_scaffold/lib/bridges/calendar_date_select/lib/as_cds_bridge.rb +53 -0
  441. data/vendor/plugins/active_scaffold/lib/bridges/file_column/bridge.rb +12 -0
  442. data/vendor/plugins/active_scaffold/lib/bridges/file_column/lib/as_file_column_bridge.rb +49 -0
  443. data/vendor/plugins/active_scaffold/lib/bridges/file_column/lib/file_column_helpers.rb +51 -0
  444. data/vendor/plugins/active_scaffold/lib/bridges/file_column/lib/form_ui.rb +32 -0
  445. data/vendor/plugins/active_scaffold/lib/bridges/file_column/lib/list_ui.rb +26 -0
  446. data/vendor/plugins/active_scaffold/lib/bridges/file_column/test/functional/file_column_keep_test.rb +43 -0
  447. data/vendor/plugins/active_scaffold/lib/bridges/file_column/test/mock_model.rb +9 -0
  448. data/vendor/plugins/active_scaffold/lib/bridges/file_column/test/test_helper.rb +15 -0
  449. data/vendor/plugins/active_scaffold/lib/dhtml_confirm.rb +54 -0
  450. data/vendor/plugins/active_scaffold/lib/extensions/action_controller_rendering.rb +20 -0
  451. data/vendor/plugins/active_scaffold/lib/extensions/action_view_rendering.rb +87 -0
  452. data/vendor/plugins/active_scaffold/lib/extensions/array.rb +7 -0
  453. data/vendor/plugins/active_scaffold/lib/extensions/component_response_with_namespacing.rb +17 -0
  454. data/vendor/plugins/active_scaffold/lib/extensions/generic_view_paths.rb +33 -0
  455. data/vendor/plugins/active_scaffold/lib/extensions/localize.rb +10 -0
  456. data/vendor/plugins/active_scaffold/lib/extensions/name_option_for_datetime.rb +12 -0
  457. data/vendor/plugins/active_scaffold/lib/extensions/nil_id_in_url_params.rb +7 -0
  458. data/vendor/plugins/active_scaffold/lib/extensions/resources.rb +26 -0
  459. data/vendor/plugins/active_scaffold/lib/extensions/reverse_associations.rb +56 -0
  460. data/vendor/plugins/active_scaffold/lib/extensions/to_label.rb +8 -0
  461. data/vendor/plugins/active_scaffold/lib/extensions/unsaved_associated.rb +61 -0
  462. data/vendor/plugins/active_scaffold/lib/extensions/unsaved_record.rb +20 -0
  463. data/vendor/plugins/active_scaffold/lib/extensions/usa_state.rb +46 -0
  464. data/vendor/plugins/active_scaffold/lib/paginator.rb +136 -0
  465. data/vendor/plugins/active_scaffold/lib/responds_to_parent.rb +68 -0
  466. data/vendor/plugins/active_scaffold/public/blank.html +33 -0
  467. data/vendor/plugins/active_scaffold/test/bridges/bridge_test.rb +47 -0
  468. data/vendor/plugins/active_scaffold/test/config/create_test.rb +55 -0
  469. data/vendor/plugins/active_scaffold/test/config/list_test.rb +74 -0
  470. data/vendor/plugins/active_scaffold/test/config/update_test.rb +17 -0
  471. data/vendor/plugins/active_scaffold/test/const_mocker.rb +36 -0
  472. data/vendor/plugins/active_scaffold/test/data_structures/action_columns_test.rb +119 -0
  473. data/vendor/plugins/active_scaffold/test/data_structures/action_link_test.rb +78 -0
  474. data/vendor/plugins/active_scaffold/test/data_structures/action_links_test.rb +78 -0
  475. data/vendor/plugins/active_scaffold/test/data_structures/actions_test.rb +25 -0
  476. data/vendor/plugins/active_scaffold/test/data_structures/association_column_test.rb +41 -0
  477. data/vendor/plugins/active_scaffold/test/data_structures/column_test.rb +161 -0
  478. data/vendor/plugins/active_scaffold/test/data_structures/columns_test.rb +69 -0
  479. data/vendor/plugins/active_scaffold/test/data_structures/error_message_test.rb +28 -0
  480. data/vendor/plugins/active_scaffold/test/data_structures/set_test.rb +86 -0
  481. data/vendor/plugins/active_scaffold/test/data_structures/sorting_test.rb +96 -0
  482. data/vendor/plugins/active_scaffold/test/data_structures/standard_column_test.rb +34 -0
  483. data/vendor/plugins/active_scaffold/test/data_structures/virtual_column_test.rb +34 -0
  484. data/vendor/plugins/active_scaffold/test/extensions/active_record_test.rb +45 -0
  485. data/vendor/plugins/active_scaffold/test/extensions/array.rb +12 -0
  486. data/vendor/plugins/active_scaffold/test/misc/active_record_permissions.rb +154 -0
  487. data/vendor/plugins/active_scaffold/test/misc/configurable_test.rb +96 -0
  488. data/vendor/plugins/active_scaffold/test/misc/constraints_test.rb +175 -0
  489. data/vendor/plugins/active_scaffold/test/misc/finder_test.rb +70 -0
  490. data/vendor/plugins/active_scaffold/test/misc/lang_test.rb +12 -0
  491. data/vendor/plugins/active_scaffold/test/model_stub.rb +39 -0
  492. data/vendor/plugins/active_scaffold/test/run_all.rb +8 -0
  493. data/vendor/plugins/active_scaffold/test/test_helper.rb +13 -0
  494. data/vendor/plugins/active_scaffold/uninstall.rb +12 -0
  495. data/vendor/plugins/active_scaffold_as_gem/init.rb +21 -0
  496. data/vendor/plugins/active_scaffold_form_observation/init.rb +4 -0
  497. data/vendor/plugins/active_scaffold_form_observation/lib/active_scaffold_form_observation.rb +99 -0
  498. data/vendor/plugins/active_scaffold_full_refresh/init.rb +7 -0
  499. data/vendor/plugins/active_scaffold_full_refresh/lib/active_scaffold_full_refresh.rb +46 -0
  500. data/vendor/plugins/active_scaffold_move_column_under/init.rb +17 -0
  501. data/vendor/plugins/acts_as_money/README +24 -0
  502. data/vendor/plugins/acts_as_money/Rakefile +22 -0
  503. data/vendor/plugins/acts_as_money/init.rb +3 -0
  504. data/vendor/plugins/acts_as_money/install.rb +1 -0
  505. data/vendor/plugins/acts_as_money/lib/acts_as_money.rb +27 -0
  506. data/vendor/plugins/acts_as_money/tasks/money_tasks.rake +4 -0
  507. data/vendor/plugins/acts_as_money/test/money_test.rb +8 -0
  508. data/vendor/plugins/association_hacks/init.rb +1 -0
  509. data/vendor/plugins/association_hacks/lib/association_hacks.rb +27 -0
  510. data/vendor/plugins/railspdf/CHANGELOG +5 -0
  511. data/vendor/plugins/railspdf/README +88 -0
  512. data/vendor/plugins/railspdf/Rakefile +22 -0
  513. data/vendor/plugins/railspdf/TODO +1 -0
  514. data/vendor/plugins/railspdf/init.rb +3 -0
  515. data/vendor/plugins/railspdf/lib/railspdf.rb +65 -0
  516. data/vendor/plugins/railspdf/test/railspdf_test.rb +14 -0
  517. data/vendor/plugins/railspdf/test/test_helper.rb +9 -0
  518. data/vendor/plugins/render_component/README +37 -0
  519. data/vendor/plugins/render_component/Rakefile +22 -0
  520. data/vendor/plugins/render_component/init.rb +2 -0
  521. data/vendor/plugins/render_component/lib/components.rb +142 -0
  522. data/vendor/plugins/render_component/test/abstract_unit.rb +8 -0
  523. data/vendor/plugins/render_component/test/components_test.rb +140 -0
  524. metadata +657 -0
@@ -0,0 +1,43 @@
1
+ class Employee::Slimtimer < ActiveRecord::Base
2
+ belongs_to :employee
3
+
4
+ has_many :owned_tasks, :class_name => 'SlimtimerTask', :foreign_key => :owner_employee_slimtimer_id
5
+ has_many :time_entries, :class_name => 'SlimtimerTimeEntry', :dependent => :destroy, :foreign_key => :employee_slimtimer_id
6
+
7
+ validates_presence_of [:employee_id, :api_key, :username, :password]
8
+ end
9
+
10
+ Employee.class_eval do
11
+ has_one :slimtimer, :class_name => 'Employee::Slimtimer', :dependent => :destroy, :foreign_key => :employee_id
12
+
13
+ def slimtimer_api_key
14
+ slimtimer.api_key unless slimtimer.nil?
15
+ end
16
+
17
+ def slimtimer_username
18
+ slimtimer.username unless slimtimer.nil?
19
+ end
20
+
21
+ def slimtimer_password
22
+ slimtimer.password unless slimtimer.nil?
23
+ end
24
+
25
+ def slimtimer_api_key=(val)
26
+ build_slimtimer if slimtimer.nil?
27
+
28
+ slimtimer.api_key = val
29
+ end
30
+
31
+ def slimtimer_username=(val)
32
+ build_slimtimer if slimtimer.nil?
33
+
34
+ slimtimer.username = val
35
+ end
36
+
37
+ def slimtimer_password=(val)
38
+ build_slimtimer if slimtimer.nil?
39
+
40
+ slimtimer.password = val
41
+ end
42
+
43
+ end
@@ -0,0 +1,20 @@
1
+ class EmployeeClientLaborRate < ActiveRecord::Base
2
+ include MoneyModelHelper
3
+
4
+ belongs_to :client
5
+ belongs_to :employee
6
+
7
+ validates_presence_of :client_id
8
+ validates_presence_of :employee_id
9
+
10
+ validates_numericality_of :hourly_rate, :greater_than_or_equal_to => 0, :message => "is not a valid monetary amount"
11
+
12
+ validates_uniqueness_of :employee_id, :scope => :client_id, :message => "/ Client relationship is already defined"
13
+
14
+ money :hourly_rate, :currency => false
15
+
16
+ def label
17
+ "%s : %s" % [self.employee.name,self.client.name]
18
+ end
19
+
20
+ end
@@ -0,0 +1,280 @@
1
+ class Invoice < ActiveRecord::Base
2
+ include ExtensibleObjectHelper
3
+
4
+ # NOTE: this has to be above the has_many, otherwise activities would get nullified before this callback had a chance to return fals
5
+ before_destroy :ensure_not_published_on_destroy
6
+ before_destroy :ensure_were_the_most_recent
7
+
8
+ before_update :ensure_not_published_on_update
9
+
10
+ after_create :reattach_activities
11
+ after_update :reattach_activities
12
+
13
+ after_update :mark_invoice_payments
14
+ after_create :mark_invoice_payments
15
+ after_destroy :remove_invoice_payments
16
+
17
+ belongs_to :client
18
+ has_many :activities, :dependent => :nullify
19
+ has_many :payments, :through => 'invoice_payments'
20
+
21
+ has_and_belongs_to_many(
22
+ :activity_types,
23
+ :join_table => 'invoices_activity_types',
24
+ :before_remove => :invalid_if_published,
25
+ :before_add => :invalid_if_published,
26
+ :uniq => true
27
+ )
28
+
29
+ validates_presence_of :client_id, :issued_on
30
+
31
+ # This just ends up being useful in a couple places
32
+ ACTIVITY_TOTAL_SQL = '(IF(activities.cost_in_cents IS NULL, 0, activities.cost_in_cents)+IF(activities.tax_in_cents IS NULL, 0, activities.tax_in_cents))'
33
+
34
+ def initialize(*args)
35
+ super(*args)
36
+ end_of_last_month = Time.utc(*Time.now.to_a).last_month.end_of_month
37
+ self.issued_on = end_of_last_month unless self.issued_on
38
+ end
39
+
40
+ def invalid_if_published(collection_record = nil)
41
+ raise "Can't adjust an already-published invoice." if !new_record? and is_published
42
+ end
43
+
44
+ def reattach_activities
45
+ included_activity_types = activity_types.collect{ |a| a.label.downcase }
46
+ unincluded_activity_types = ActivityType.find(:all).collect{ |a| a.label.downcase } - included_activity_types
47
+
48
+ # First we NULL'ify (remove) existing attachments that no longer should be:
49
+ nullify_conditions = []
50
+ nullify_parameters = []
51
+
52
+ # Conditions for occurance adjutments
53
+ nullify_conditions << '(DATEDIFF(occurred_on, DATE(?)) > 0)'
54
+ nullify_parameters << issued_on
55
+
56
+ # For the ActivityType Adjustments:
57
+ unless unincluded_activity_types.empty?
58
+ nullify_conditions << '(%s)' % ( ['activity_type = ?'] * unincluded_activity_types.size).join(' OR ')
59
+ nullify_parameters += unincluded_activity_types
60
+ end
61
+
62
+ Activity.update_all(
63
+ 'invoice_id = NULL',
64
+ [ ['invoice_id = ?', 'is_published = ?', ('(%s)' % nullify_conditions.join(' OR ')) ].join(' AND ') ]+
65
+ [id, true]+nullify_parameters
66
+ ) unless new_record?
67
+
68
+ # Now we attach the new records :
69
+ update_where = [
70
+ ['invoice_id IS NULL'],
71
+ ['is_published = ?', true],
72
+ ['client_id = ?', client_id],
73
+ ['DATEDIFF(occurred_on, DATE(?)) <= 0', issued_on],
74
+
75
+ # Slightly more complicated, for the type includes:
76
+ ( (included_activity_types.size > 0) ?
77
+ [ '('+(['activity_type = ?'] * included_activity_types.size).join(' OR ')+')', included_activity_types ] :
78
+ [ 'activity_type IS NULL' ] )
79
+ ]
80
+
81
+ Activity.update_all(
82
+ ['invoice_id = ?', id ],
83
+ # This is what ActiveRecord actually expects...
84
+ update_where.collect{|c| c[0]}.join(' AND ').to_a + update_where.reject{|c| c.length < 2 }.collect{|c| c[1]}.flatten
85
+ )
86
+ end
87
+
88
+ def is_most_recent_invoice?
89
+ newest_invoice = Invoice.find :first, :select => 'id', :order => 'issued_on DESC', :conditions => ['client_id = ?', client_id]
90
+
91
+ (newest_invoice.nil? or newest_invoice.id == id) ? true : false
92
+ end
93
+
94
+ def ensure_were_the_most_recent
95
+ unless is_most_recent_invoice?
96
+ errors.add_to_base "Can't destroy an invoice if its not the client's most recent invoice"
97
+ return false
98
+ end
99
+ end
100
+
101
+ def ensure_not_published_on_destroy
102
+ if is_published and !changes.has_key? :is_published
103
+ errors.add_to_base "Can't destroy a published invoice"
104
+ return false
105
+ end
106
+ end
107
+
108
+ def ensure_not_published_on_update
109
+ errors.add_to_base "Can't update a published invoice" if is_published and !changes.has_key? :is_published
110
+ end
111
+
112
+ def validate_on_update
113
+ errors.add :client, "can't be updated after creation" if changes.has_key? "client_id"
114
+
115
+ errors.add_to_base(
116
+ "Invoice can't be updated once published."
117
+ ) if is_published and changes.reject{|k,v| k == 'is_published'}.length > 0
118
+
119
+ errors.add_to_base(
120
+ "Invoice can't be unpublished, unless its the newest invoice in the client's queue."
121
+ ) if changes.has_key?('is_published') and is_published_was and !is_most_recent_invoice?
122
+ end
123
+
124
+ def taxes_total
125
+ process_total :taxes_total, :tax_in_cents
126
+ end
127
+
128
+ def sub_total
129
+ process_total :sub_total, :cost_in_cents
130
+ end
131
+
132
+ def amount( force_reload = false )
133
+ (attribute_present? :amount_in_cents and !force_reload) ?
134
+ Money.new(read_attribute(:amount_in_cents).to_i) :
135
+ process_total( :amount, ACTIVITY_TOTAL_SQL )
136
+ end
137
+
138
+ def grand_total
139
+ process_total :grand_total, ACTIVITY_TOTAL_SQL
140
+ end
141
+
142
+ def name
143
+ '"%s" Invoice on %s' % [ (client) ? client.company_name : '(Unknown Client)', issued_on.strftime("%m/%d/%Y %I:%M %p") ]
144
+ end
145
+
146
+ def long_name
147
+ "Invoice #%d (%s) - %s (%s)" % [
148
+ id,
149
+ issued_on.strftime("%m/%d/%Y %I:%M %p"),
150
+ client.company_name,
151
+ ('$%.2f' % amount.to_s).gsub(/(\d)(?=\d{3}+(\.\d*)?$)/, '\1,')
152
+ ]
153
+ end
154
+
155
+ def remove_invoice_payments
156
+ InvoicePayment.destroy_all ['invoice_id = ?', id]
157
+ end
158
+
159
+ def mark_invoice_payments
160
+ if changes.has_key? "is_published"
161
+ remove_invoice_payments
162
+
163
+ if is_published
164
+ unallocated_payments = Payment.find_with_totals(
165
+ :all,
166
+ :conditions => [
167
+ 'client_id = ? AND (payments.amount_in_cents - IF(payments_total.amount_allocated_in_cents IS NULL, 0, payments_total.amount_allocated_in_cents) ) > ?',
168
+ client_id,
169
+ 0
170
+ ]
171
+ )
172
+
173
+ current_client_balance = 0.0.to_money
174
+ unallocated_payments.each { |pmnt| current_client_balance -= pmnt.amount_unallocated }
175
+
176
+ invoice_balance = amount
177
+
178
+ unallocated_payments.each do |unallocated_pmnt|
179
+ break if invoice_balance == 0 or current_client_balance >= 0
180
+
181
+ payment_allocation = (unallocated_pmnt.amount_unallocated > invoice_balance) ?
182
+ invoice_balance :
183
+ unallocated_pmnt.amount_unallocated
184
+
185
+ InvoicePayment.create! :invoice => self, :payment => unallocated_pmnt, :amount => payment_allocation
186
+
187
+ invoice_balance -= payment_allocation
188
+ current_client_balance += payment_allocation
189
+ end
190
+ end
191
+ end
192
+
193
+ end
194
+
195
+ def paid_on
196
+ raise StandardError unless is_paid?
197
+
198
+ InvoicePayment.find(
199
+ :first,
200
+ :order => 'payments.paid_on DESC',
201
+ :include => [:payment],
202
+ :conditions => ['invoice_id = ?', id]
203
+ ).payment.paid_on
204
+
205
+ rescue
206
+ nil
207
+ end
208
+
209
+ def is_paid?( force_reload = false )
210
+ (attribute_present? :is_paid and !force_reload) ?
211
+ (read_attribute(:is_paid).to_i == 1) :
212
+ amount_outstanding.zero?
213
+ end
214
+
215
+ def amount_paid( force_reload = false )
216
+ Money.new(
217
+ (attribute_present? :amount_paid_in_cents and !force_reload) ?
218
+ read_attribute(:amount_paid_in_cents).to_i :
219
+ InvoicePayment.sum( :amount_in_cents, :conditions => ['invoice_id = ?', id] ).to_i
220
+ )
221
+ end
222
+
223
+ def amount_outstanding
224
+ amount - amount_paid
225
+ end
226
+
227
+ def self.find_with_totals( how_many = :all, options = {} )
228
+ joins = []
229
+ joins << 'LEFT JOIN ('+
230
+ "SELECT invoices.id AS invoice_id, SUM(#{ACTIVITY_TOTAL_SQL}) AS total_in_cents"+
231
+ ' FROM invoices'+
232
+ ' LEFT JOIN activities ON activities.invoice_id = invoices.id'+
233
+ ' GROUP BY invoices.id'+
234
+ ') AS activities_total ON activities_total.invoice_id = invoices.id'
235
+
236
+ joins << 'LEFT JOIN ('+
237
+ 'SELECT invoices.id AS invoice_id, SUM(invoice_payments.amount_in_cents) AS total_in_cents'+
238
+ ' FROM invoices'+
239
+ ' LEFT JOIN invoice_payments ON invoice_payments.invoice_id = invoices.id'+
240
+ ' GROUP BY invoices.id'+
241
+ ') AS invoices_total ON invoices_total.invoice_id = invoices.id'
242
+
243
+ cast_amount = 'IF(activities_total.total_in_cents IS NULL, 0,activities_total.total_in_cents)'
244
+ cast_amount_paid = 'IF(invoices_total.total_in_cents IS NULL, 0,invoices_total.total_in_cents)'
245
+
246
+ Invoice.find(
247
+ how_many,
248
+ {
249
+ :select => [
250
+ 'invoices.id',
251
+ 'invoices.client_id',
252
+ 'invoices.comments',
253
+ 'invoices.issued_on',
254
+ "#{cast_amount} AS amount_in_cents",
255
+ "#{cast_amount_paid} AS amount_paid_in_cents",
256
+ "#{cast_amount} - #{cast_amount_paid} AS amount_outstanding_in_cents"
257
+ ].join(', '),
258
+ :order => 'issued_on ASC',
259
+ :joins => joins.join(' ')
260
+ }.merge(options)
261
+ )
262
+ end
263
+
264
+ def authorized_for?(options)
265
+ case options[:action].to_s
266
+ when /^(update|destroy)$/
267
+ (is_published and !is_most_recent_invoice?) ? false : true
268
+ else
269
+ true
270
+ end
271
+ end
272
+
273
+ private
274
+
275
+ def process_total(name, field_sql)
276
+ Money.new Activity.sum(field_sql, :conditions => ['invoice_id = ?', id]).to_i
277
+ end
278
+
279
+ handle_extensions
280
+ end
@@ -0,0 +1,8 @@
1
+ class InvoicePayment < ActiveRecord::Base
2
+ include MoneyModelHelper
3
+
4
+ belongs_to :payment
5
+ belongs_to :invoice
6
+
7
+ money :amount, :currency => false
8
+ end
@@ -0,0 +1,85 @@
1
+ class Notifier < ActionMailer::Base
2
+
3
+ def reset_password_requested( credential_name, credential_email, reset_token, host )
4
+ tmpl_parameters = {
5
+ :reset_token => reset_token,
6
+ :credential_name => credential_name,
7
+ :credential_email => credential_email,
8
+ :reset_password_url_for => {
9
+ :controller => 'authentication',
10
+ :action => 'reset_password_via_token',
11
+ :email_address => credential_email,
12
+ :token => reset_token,
13
+ :only_path => false,
14
+ :host => host
15
+ }
16
+ }.merge common_tmpl_parameters
17
+
18
+ from from_address(tmpl_parameters)
19
+ bcc from_address(tmpl_parameters)
20
+ recipients '%s <%s>' % [credential_name, credential_email]
21
+ subject "%s - Password Reset Instructions" % tmpl_parameters[:company_name]
22
+
23
+ generate_view_parts :reset_password_requested, tmpl_parameters
24
+ end
25
+
26
+ def invoice_available( invoice, client, payment_notice, attachments )
27
+ tmpl_parameters = {
28
+ :client => client,
29
+ :payment_notice => payment_notice
30
+ }.merge common_tmpl_parameters
31
+
32
+ notifiers_bcc = Setting.grab(:bcc_invoices_to)[0].split ','
33
+
34
+ representative_emails = client.client_representatives.collect{|cr| '%s <%s>' % [cr.name,cr.email_address]}
35
+ is_production = (RAILS_ENV == 'production')
36
+
37
+ subject_line = "%s invoice %d (%s) now available"
38
+ subject_line += " | Intended Recipients: "+representative_emails.join(', ') unless is_production
39
+
40
+ # Here's the real deal
41
+ from from_address(tmpl_parameters)
42
+ bcc notifiers_bcc if is_production
43
+ recipients (is_production) ? representative_emails : from_address(tmpl_parameters)
44
+ subject subject_line % [
45
+ tmpl_parameters[:company_name],
46
+ invoice.id,
47
+ invoice.issued_on.strftime('%m/%d/%Y'),
48
+ representative_emails.join(', ')
49
+ ]
50
+
51
+ generate_view_parts :invoice_available, tmpl_parameters
52
+
53
+ attachments.each { |attach_params| attachment attach_params }
54
+ end
55
+
56
+ private
57
+
58
+ def common_tmpl_parameters
59
+ tmpl = {}
60
+ setting_keys = %w(
61
+ site_admin_name site_admin_email company_logo_file company_name company_address1 company_address2
62
+ company_city company_state company_zip company_phone company_fax company_url
63
+ ).collect{|s| s.to_sym}
64
+
65
+ setting_vals = Setting.grab(*setting_keys)
66
+
67
+ 0.upto(setting_keys.length-1){ |i| tmpl[setting_keys[i]] = setting_vals[i] }
68
+
69
+ tmpl
70
+ end
71
+
72
+ def from_address(tmpl)
73
+ '%s <%s>' % [ tmpl[:site_admin_name], tmpl[:site_admin_email] ]
74
+ end
75
+
76
+ def generate_view_parts(view_name, tmpl)
77
+ part( :content_type => "multipart/alternative" ) do |p|
78
+ [
79
+ { :content_type => "text/plain", :body => render_message("#{view_name.to_s}.plain.rhtml", tmpl) },
80
+ { :content_type => "text/html", :body => render_message("#{view_name.to_s}.html.rhtml", tmpl.merge({:part_container => p})) }
81
+ ].each { |parms| p.part parms.merge( { :charset => "UTF-8", :transfer_encoding => "7bit"} ) }
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,108 @@
1
+ class Payment < ActiveRecord::Base
2
+ include ExtensibleObjectHelper
3
+ include MoneyModelHelper
4
+
5
+ after_create :create_invoice_payments
6
+ after_destroy :destroy_invoice_payments
7
+ # NOTE: after_update is not needed , because payment's can't be updated...
8
+
9
+ belongs_to :client
10
+ belongs_to :payment_method
11
+
12
+ has_many :invoices, :through => 'invoice_payments'
13
+
14
+ validates_presence_of :client_id, :payment_method_id
15
+ validates_numericality_of :amount, :allow_nil => false
16
+
17
+ money :amount, :currency => false
18
+
19
+ def initialize(*args)
20
+ super(*args)
21
+ self.paid_on = Time.now.beginning_of_day if paid_on.nil?
22
+ end
23
+
24
+ def create_invoice_payments
25
+ # NOTE: Orders by oldest outstanding date first:
26
+ unpaid_invoices = Invoice.find_with_totals(
27
+ :all,
28
+ :conditions => [
29
+ [
30
+ 'client_id = ?',
31
+ 'IF(activities_total.total_in_cents IS NULL, 0,activities_total.total_in_cents) - '+
32
+ 'IF(invoices_total.total_in_cents IS NULL, 0,invoices_total.total_in_cents) > ?'
33
+ ].join(' AND '),
34
+ client_id,
35
+ 0
36
+ ]
37
+ )
38
+
39
+ current_client_balance = Money.new(0)
40
+ unpaid_invoices.each { |inv| current_client_balance += inv.amount_outstanding }
41
+
42
+ currently_unallocated = amount_unallocated
43
+
44
+ unpaid_invoices.each do |unpaid_invoice|
45
+ break if currently_unallocated <= 0 or current_client_balance <= 0
46
+
47
+ payment_allocation = (currently_unallocated >= unpaid_invoice.amount_outstanding) ?
48
+ unpaid_invoice.amount_outstanding :
49
+ currently_unallocated
50
+
51
+ InvoicePayment.create! :payment => self, :invoice => unpaid_invoice, :amount => payment_allocation
52
+
53
+ current_client_balance -= payment_allocation
54
+ currently_unallocated -= payment_allocation
55
+ end
56
+ end
57
+
58
+ def destroy_invoice_payments
59
+ InvoicePayment.destroy_all ['payment_id = ?', id]
60
+ end
61
+
62
+ def amount_unallocated
63
+ (attribute_present? :amount_unallocated_in_cents) ?
64
+ Money.new(read_attribute(:amount_unallocated_in_cents).to_i) :
65
+ (amount - amount_allocated)
66
+ end
67
+
68
+ def amount_allocated
69
+ Money.new(
70
+ (attribute_present? :amount_allocated_in_cents) ?
71
+ read_attribute(:amount_allocated_in_cents).to_i :
72
+ ( InvoicePayment.sum(:amount_in_cents, :conditions => ['payment_id = ?', id]) || 0 )
73
+ )
74
+ end
75
+
76
+ def validate_on_update
77
+ errors.add_to_base "Payments can't be updated after creation"
78
+ end
79
+
80
+ def name
81
+ '%.2f Payment from %s' % [ amount, client.company_name ]
82
+ end
83
+
84
+ def self.find_with_totals( how_many = :all, options = {} )
85
+ cast_amount_allocated = 'IF(payments_total.amount_allocated_in_cents IS NULL, 0, payments_total.amount_allocated_in_cents)'
86
+
87
+ joins = 'LEFT JOIN ('+
88
+ 'SELECT payment_id, SUM(amount_in_cents) AS amount_allocated_in_cents FROM invoice_payments GROUP BY payment_id'+
89
+ ') AS payments_total ON payments_total.payment_id = payments.id'
90
+
91
+ Payment.find(
92
+ how_many,
93
+ {
94
+ :select => [
95
+ 'payments.id',
96
+ 'payments.amount_in_cents',
97
+ "#{cast_amount_allocated} AS amount_allocated_in_cents",
98
+ "payments.amount_in_cents - #{cast_amount_allocated} AS amount_unallocated_in_cents"
99
+ ].join(', '),
100
+ :order => 'paid_on ASC',
101
+ :joins => joins,
102
+ :group => 'payments.id'
103
+ }.merge(options)
104
+ )
105
+ end
106
+
107
+ handle_extensions
108
+ end