nimbleshop_core 0.0.1.rc1

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 (359) hide show
  1. data/README.md +13 -0
  2. data/Rakefile +15 -0
  3. data/app/assets/images/engines/nimbleshop_authorizedotnet/american_express.png +0 -0
  4. data/app/assets/images/engines/nimbleshop_authorizedotnet/authorize_net_big.png +0 -0
  5. data/app/assets/images/engines/nimbleshop_authorizedotnet/authorizedotnet_big.png +0 -0
  6. data/app/assets/images/engines/nimbleshop_authorizedotnet/discover.png +0 -0
  7. data/app/assets/images/engines/nimbleshop_authorizedotnet/mastercard.png +0 -0
  8. data/app/assets/images/engines/nimbleshop_authorizedotnet/visa.png +0 -0
  9. data/app/assets/images/engines/nimbleshop_cod/cod_small.png +0 -0
  10. data/app/assets/images/engines/nimbleshop_paypalwp/paypal_big.png +0 -0
  11. data/app/assets/images/engines/nimbleshop_paypalwp/paypal_small.png +0 -0
  12. data/app/assets/images/engines/nimbleshop_splitable/splitable.png +0 -0
  13. data/app/assets/images/engines/nimbleshop_splitable/splitable_big.png +0 -0
  14. data/app/assets/images/engines/nimbleshop_splitable/splitable_small.png +0 -0
  15. data/app/assets/javascripts/admin/products/edit.js.coffee +19 -0
  16. data/app/assets/javascripts/admin/shipping_methods/edit.js.coffee +6 -0
  17. data/app/assets/javascripts/admin.js +25 -0
  18. data/app/assets/javascripts/fadeout_success.js.coffee +2 -0
  19. data/app/assets/javascripts/payment_method.js.coffee +19 -0
  20. data/app/assets/javascripts/product-group-condition.js.coffee +33 -0
  21. data/app/assets/javascripts/remove_deletion_of_product_pictures.js.coffee +5 -0
  22. data/app/assets/javascripts/shipit.js.coffee +10 -0
  23. data/app/assets/javascripts/twitter_bootstrap.js.coffee +4 -0
  24. data/app/assets/stylesheets/admin.css +9 -0
  25. data/app/assets/stylesheets/admin_bootstrap_and_overrides.css.less +38 -0
  26. data/app/assets/stylesheets/admin_custom.css +104 -0
  27. data/app/assets/twitter/bootstrap/glyphicons-halflings-white.png +0 -0
  28. data/app/assets/twitter/bootstrap/glyphicons-halflings.png +0 -0
  29. data/app/controllers/admin/custom_fields_controller.rb +56 -0
  30. data/app/controllers/admin/link_groups_controller.rb +76 -0
  31. data/app/controllers/admin/main_controller.rb +9 -0
  32. data/app/controllers/admin/navigations_controller.rb +49 -0
  33. data/app/controllers/admin/orders_controller.rb +47 -0
  34. data/app/controllers/admin/payment_methods_controller.rb +26 -0
  35. data/app/controllers/admin/product_groups_controller.rb +70 -0
  36. data/app/controllers/admin/products_controller.rb +71 -0
  37. data/app/controllers/admin/shipments_controller.rb +19 -0
  38. data/app/controllers/admin/shipping_methods_controller.rb +93 -0
  39. data/app/controllers/admin/shipping_zones_controller.rb +62 -0
  40. data/app/controllers/admin/shops_controller.rb +33 -0
  41. data/app/controllers/admin_controller.rb +38 -0
  42. data/app/helpers/admin_helper.rb +83 -0
  43. data/app/helpers/nimbleshop_helper.rb +43 -0
  44. data/app/helpers/payment_method_helper.rb +52 -0
  45. data/app/mailers/admin_mailer.rb +22 -0
  46. data/app/mailers/mailer.rb +31 -0
  47. data/app/models/address.rb +69 -0
  48. data/app/models/country_shipping_zone.rb +36 -0
  49. data/app/models/creditcard.rb +95 -0
  50. data/app/models/custom_field.rb +8 -0
  51. data/app/models/custom_field_answer/field.rb +101 -0
  52. data/app/models/custom_field_answer.rb +17 -0
  53. data/app/models/line_item.rb +48 -0
  54. data/app/models/link.rb +3 -0
  55. data/app/models/link_group.rb +8 -0
  56. data/app/models/navigation.rb +7 -0
  57. data/app/models/order.rb +158 -0
  58. data/app/models/payment_method.rb +20 -0
  59. data/app/models/payment_transaction.rb +11 -0
  60. data/app/models/picture.rb +30 -0
  61. data/app/models/product.rb +76 -0
  62. data/app/models/product_group.rb +65 -0
  63. data/app/models/product_group_condition.rb +8 -0
  64. data/app/models/regional_shipping_zone.rb +25 -0
  65. data/app/models/shipment.rb +19 -0
  66. data/app/models/shipment_carrier.rb +3 -0
  67. data/app/models/shipping_cost_calculator.rb +9 -0
  68. data/app/models/shipping_method.rb +103 -0
  69. data/app/models/shipping_zone.rb +9 -0
  70. data/app/models/shop.rb +26 -0
  71. data/app/models/tax_calculator.rb +18 -0
  72. data/app/observers/order_observer.rb +53 -0
  73. data/app/uploaders/picture_uploader.rb +65 -0
  74. data/app/views/admin/custom_fields/_form.html.erb +45 -0
  75. data/app/views/admin/custom_fields/edit.html.erb +7 -0
  76. data/app/views/admin/custom_fields/index.html.erb +31 -0
  77. data/app/views/admin/custom_fields/new.html.erb +7 -0
  78. data/app/views/admin/link_groups/_form.html.erb +31 -0
  79. data/app/views/admin/link_groups/_link_group.html.erb +50 -0
  80. data/app/views/admin/link_groups/_link_group_title.html.erb +5 -0
  81. data/app/views/admin/link_groups/edit.html.erb +8 -0
  82. data/app/views/admin/link_groups/index.html.erb +29 -0
  83. data/app/views/admin/link_groups/new.html.erb +9 -0
  84. data/app/views/admin/mailer/new_order_notification.text.erb +31 -0
  85. data/app/views/admin/main/index.html.erb +5 -0
  86. data/app/views/admin/navigations/new.html.erb +29 -0
  87. data/app/views/admin/orders/_line_item.html.erb +11 -0
  88. data/app/views/admin/orders/_line_items.html.erb +39 -0
  89. data/app/views/admin/orders/_payment_info.html.erb +20 -0
  90. data/app/views/admin/orders/_shipping_info.html.erb +40 -0
  91. data/app/views/admin/orders/index.html.erb +33 -0
  92. data/app/views/admin/orders/show.html.erb +40 -0
  93. data/app/views/admin/payment_methods/index.html.erb +6 -0
  94. data/app/views/admin/product_groups/_condition.html.mustache +20 -0
  95. data/app/views/admin/product_groups/_form.html.erb +72 -0
  96. data/app/views/admin/product_groups/edit.html.erb +7 -0
  97. data/app/views/admin/product_groups/index.html.erb +30 -0
  98. data/app/views/admin/product_groups/new.html.erb +7 -0
  99. data/app/views/admin/product_groups/show.html.erb +9 -0
  100. data/app/views/admin/products/_custom_field_answer.html.erb +9 -0
  101. data/app/views/admin/products/_form.html.erb +69 -0
  102. data/app/views/admin/products/_handle_pictures.html.erb +32 -0
  103. data/app/views/admin/products/_pictures.html.erb +33 -0
  104. data/app/views/admin/products/edit.html.erb +26 -0
  105. data/app/views/admin/products/index.html.erb +34 -0
  106. data/app/views/admin/products/new.html.erb +9 -0
  107. data/app/views/admin/shipping_methods/_form.html.erb +58 -0
  108. data/app/views/admin/shipping_methods/_shipping_method.html.erb +31 -0
  109. data/app/views/admin/shipping_methods/edit.html.erb +18 -0
  110. data/app/views/admin/shipping_methods/new.html.erb +7 -0
  111. data/app/views/admin/shipping_zones/_form.html.erb +31 -0
  112. data/app/views/admin/shipping_zones/_shipping_method.html.erb +13 -0
  113. data/app/views/admin/shipping_zones/_shipping_zone.html.erb +47 -0
  114. data/app/views/admin/shipping_zones/edit.html.erb +8 -0
  115. data/app/views/admin/shipping_zones/index.html.erb +16 -0
  116. data/app/views/admin/shipping_zones/new.html.erb +8 -0
  117. data/app/views/admin/shops/edit.html.erb +119 -0
  118. data/app/views/layouts/_admin_footer.html.erb +23 -0
  119. data/app/views/layouts/_admin_sidebar.html.erb +0 -0
  120. data/app/views/layouts/_container.html.erb +33 -0
  121. data/app/views/layouts/_top_nav_bar.html.erb +30 -0
  122. data/app/views/layouts/admin.html.erb +35 -0
  123. data/app/views/layouts/payment_method.html.erb +12 -0
  124. data/app/views/mailer/order_notification_to_buyer.text.erb +31 -0
  125. data/app/views/mailer/shipment_notification_to_buyer.text.erb +34 -0
  126. data/config/initializers/001_settings_application.rb +10 -0
  127. data/config/initializers/002_load_core_extensions.rb +8 -0
  128. data/config/initializers/field_with_error.rb +2 -0
  129. data/config/initializers/strip_and_nullify.rb +13 -0
  130. data/config/initializers/time_formats.rb +1 -0
  131. data/config/locales/en.yml +61 -0
  132. data/config/routes.rb +56 -0
  133. data/db/development.sqlite3 +0 -0
  134. data/db/migrate/20111022144421_create_product_groups.rb +12 -0
  135. data/db/migrate/20111022144426_create_products.rb +16 -0
  136. data/db/migrate/20111022144653_create_orders.rb +21 -0
  137. data/db/migrate/20111022144725_create_line_items.rb +15 -0
  138. data/db/migrate/20111022234951_create_shops.rb +20 -0
  139. data/db/migrate/20111026193227_create_link_groups.rb +12 -0
  140. data/db/migrate/20111026193435_create_navigations.rb +10 -0
  141. data/db/migrate/20111031135810_create_links.rb +10 -0
  142. data/db/migrate/20111106193938_create_custom_fields.rb +10 -0
  143. data/db/migrate/20111106195112_create_custom_field_answers.rb +14 -0
  144. data/db/migrate/20111106201548_create_product_group_conditions.rb +12 -0
  145. data/db/migrate/20111107035350_create_creditcard_transactions.rb +15 -0
  146. data/db/migrate/20111109190754_create_addresses.rb +24 -0
  147. data/db/migrate/20111125173344_create_pictures.rb +16 -0
  148. data/db/migrate/20111201024408_create_shipping_methods.rb +16 -0
  149. data/db/migrate/20111202143154_create_shipping_zones.rb +17 -0
  150. data/db/migrate/20111203034020_create_payment_methods.rb +14 -0
  151. data/db/migrate/20111221190000_create_preferences.rb +14 -0
  152. data/db/migrate/20120105234047_create_shipments.rb +11 -0
  153. data/db/migrate/20120105234553_create_shipment_carriers.rb +11 -0
  154. data/db/migrate/20120606165355_create_delayed_jobs.rb +22 -0
  155. data/db/production.sqlite3 +0 -0
  156. data/db/schema.rb +265 -0
  157. data/db/seeds.rb +1 -0
  158. data/db/test.sqlite3 +0 -0
  159. data/lib/assets/javascripts/loading_state_for_buttons.js.coffee +3 -0
  160. data/lib/assets/javascripts/show_help_on_hover.js.coffee +2 -0
  161. data/lib/assets/javascripts/utils/autoresize.js.coffee +3 -0
  162. data/lib/assets/javascripts/utils/focus.js.coffee +3 -0
  163. data/lib/assets/javascripts/utils/formatNumber.js.coffee +25 -0
  164. data/lib/assets/javascripts/utils/formatPrice.js.coffee +24 -0
  165. data/lib/generators/nimbleshop/app/app_generator.rb +84 -0
  166. data/lib/generators/nimbleshop/app/templates/config/application.yml +61 -0
  167. data/lib/generators/nimbleshop/app/templates/config/initializers/airbrake.rb +3 -0
  168. data/lib/generators/nimbleshop/app/templates/config/initializers/cache_images.rb +4 -0
  169. data/lib/generators/nimbleshop/app/templates/config/initializers/carrierwave.rb +36 -0
  170. data/lib/generators/nimbleshop/app/templates/config/initializers/delayed_job.rb +15 -0
  171. data/lib/generators/nimbleshop/app/templates/config/initializers/setup_email.rb +24 -0
  172. data/lib/nimbleshop/core_ext/activerecord_base.rb +18 -0
  173. data/lib/nimbleshop/core_ext/filelessio.rb +4 -0
  174. data/lib/nimbleshop/core_ext/force_ssl.rb +26 -0
  175. data/lib/nimbleshop/core_ext/mustache_templater_handler.rb +10 -0
  176. data/lib/nimbleshop/engine.rb +23 -0
  177. data/lib/nimbleshop/permalink/builder.rb +46 -0
  178. data/lib/nimbleshop/processor/base.rb +51 -0
  179. data/lib/nimbleshop/processor.rb +3 -0
  180. data/lib/nimbleshop/product_group_search/base_field.rb +19 -0
  181. data/lib/nimbleshop/product_group_search/core_field_strategy.rb +31 -0
  182. data/lib/nimbleshop/product_group_search/custom_field_strategy.rb +39 -0
  183. data/lib/nimbleshop/product_group_search/date_field.rb +16 -0
  184. data/lib/nimbleshop/product_group_search/extension.rb +46 -0
  185. data/lib/nimbleshop/product_group_search/number_field.rb +17 -0
  186. data/lib/nimbleshop/product_group_search/text_field.rb +33 -0
  187. data/lib/nimbleshop/product_group_search.rb +74 -0
  188. data/lib/nimbleshop/sampledata/data.rb +246 -0
  189. data/lib/nimbleshop/sampledata/pictures/product1/pic1.jpg +0 -0
  190. data/lib/nimbleshop/sampledata/pictures/product2/pic1.jpg +0 -0
  191. data/lib/nimbleshop/sampledata/pictures/product2/pic2.jpg +0 -0
  192. data/lib/nimbleshop/sampledata/pictures/product3/pic1.jpg +0 -0
  193. data/lib/nimbleshop/sampledata/pictures/product3/pic2.jpg +0 -0
  194. data/lib/nimbleshop/sampledata/pictures/product3/pic3.jpg +0 -0
  195. data/lib/nimbleshop/sampledata/pictures/product3/pic4.jpg +0 -0
  196. data/lib/nimbleshop/sampledata/pictures/product3/pic5.jpg +0 -0
  197. data/lib/nimbleshop/sampledata/pictures/product4/pic1.jpg +0 -0
  198. data/lib/nimbleshop/sampledata/pictures/product4/pic2.jpg +0 -0
  199. data/lib/nimbleshop/sampledata/pictures/product5/pic1.jpg +0 -0
  200. data/lib/nimbleshop/sampledata/pictures/product5/pic2.jpg +0 -0
  201. data/lib/nimbleshop/sampledata/pictures/product6/pic1.jpg +0 -0
  202. data/lib/nimbleshop/sampledata/pictures/product6/pic10.jpg +0 -0
  203. data/lib/nimbleshop/sampledata/pictures/product6/pic11.jpg +0 -0
  204. data/lib/nimbleshop/sampledata/pictures/product6/pic12.jpg +0 -0
  205. data/lib/nimbleshop/sampledata/pictures/product6/pic13.jpg +0 -0
  206. data/lib/nimbleshop/sampledata/pictures/product6/pic14.jpg +0 -0
  207. data/lib/nimbleshop/sampledata/pictures/product6/pic15.jpg +0 -0
  208. data/lib/nimbleshop/sampledata/pictures/product6/pic16.jpg +0 -0
  209. data/lib/nimbleshop/sampledata/pictures/product6/pic17.jpg +0 -0
  210. data/lib/nimbleshop/sampledata/pictures/product6/pic2.jpg +0 -0
  211. data/lib/nimbleshop/sampledata/pictures/product6/pic3.jpg +0 -0
  212. data/lib/nimbleshop/sampledata/pictures/product6/pic4.jpg +0 -0
  213. data/lib/nimbleshop/sampledata/pictures/product6/pic5.jpg +0 -0
  214. data/lib/nimbleshop/sampledata/pictures/product6/pic6.jpg +0 -0
  215. data/lib/nimbleshop/sampledata/pictures/product6/pic7.jpg +0 -0
  216. data/lib/nimbleshop/sampledata/pictures/product6/pic8.jpg +0 -0
  217. data/lib/nimbleshop/sampledata/pictures/product6/pic9.jpg +0 -0
  218. data/lib/nimbleshop/sampledata/pictures/product7/pic1.jpg +0 -0
  219. data/lib/nimbleshop/sampledata/pictures/product7/pic2.jpg +0 -0
  220. data/lib/nimbleshop/sampledata/pictures/product7/pic3.jpg +0 -0
  221. data/lib/nimbleshop/sampledata/pictures/product8/pic1.jpg +0 -0
  222. data/lib/nimbleshop/sampledata/pictures/product8/pic2.jpg +0 -0
  223. data/lib/nimbleshop/sampledata/pictures/product8/pic3.jpg +0 -0
  224. data/lib/nimbleshop/sampledata/pictures/product8/pic4.jpg +0 -0
  225. data/lib/nimbleshop/sampledata/pictures/product8/pic5.jpg +0 -0
  226. data/lib/nimbleshop/sampledata/pictures/product8/pic6.jpg +0 -0
  227. data/lib/nimbleshop/sampledata/pictures/product8/pic7.jpg +0 -0
  228. data/lib/nimbleshop/sampledata/pictures/product8/pic8.jpg +0 -0
  229. data/lib/nimbleshop/sampledata/pictures/product9/pic1.jpg +0 -0
  230. data/lib/nimbleshop/sampledata/pictures/product9/pic2.jpg +0 -0
  231. data/lib/nimbleshop/util.rb +60 -0
  232. data/lib/nimbleshop/version.rb +18 -0
  233. data/lib/nimbleshop_core.rb +23 -0
  234. data/lib/tasks/localhost2public.rake +8 -0
  235. data/lib/tasks/setup.rake +26 -0
  236. data/test/factories/address_factory.rb +31 -0
  237. data/test/factories/creditcard_factory.rb +30 -0
  238. data/test/factories/custom_field_answer_factory.rb +6 -0
  239. data/test/factories/custom_field_factory.rb +21 -0
  240. data/test/factories/line_item_factory.rb +7 -0
  241. data/test/factories/link_factory.rb +6 -0
  242. data/test/factories/link_group_factory.rb +5 -0
  243. data/test/factories/navigation_factory.rb +6 -0
  244. data/test/factories/order_factory.rb +49 -0
  245. data/test/factories/payment_method_factory.rb +6 -0
  246. data/test/factories/payment_transaction_factory.rb +42 -0
  247. data/test/factories/picture_factory.rb +11 -0
  248. data/test/factories/product_group_condition_factory.rb +40 -0
  249. data/test/factories/product_group_factory.rb +5 -0
  250. data/test/factories/products_factory.rb +11 -0
  251. data/test/factories/shipment_carrier_factory.rb +6 -0
  252. data/test/factories/shipment_factory.rb +6 -0
  253. data/test/factories/shipping_method_factory.rb +15 -0
  254. data/test/factories/shipping_zone_factory.rb +5 -0
  255. data/test/factories/shop_factory.rb +8 -0
  256. data/test/fixtures/files/avatar.png +0 -0
  257. data/test/fixtures/link_groups.yml +13 -0
  258. data/test/fixtures/payment_methods.yml +39 -0
  259. data/test/fixtures/shops.yml +15 -0
  260. data/test/helpers/admin_helper_test.rb +37 -0
  261. data/test/helpers/nimbleshop_helper_test.rb +17 -0
  262. data/test/integration/admin/link_groups_acceptance_test.rb +39 -0
  263. data/test/integration/admin/orders_acceptance_test.rb +63 -0
  264. data/test/integration/admin/payment_methods_acceptance_test.rb +13 -0
  265. data/test/integration/admin/product_groups_acceptance_test.rb +89 -0
  266. data/test/integration/admin/products_acceptance_test.rb +105 -0
  267. data/test/integration/admin/shipping_methods_acceptance_test.rb +63 -0
  268. data/test/integration/admin/shipping_zones_acceptance_test.rb +16 -0
  269. data/test/integration/admin/shops_acceptance_test.rb +44 -0
  270. data/test/integration/checkout/cart_acceptance_test.rb +113 -0
  271. data/test/integration/checkout/payment_confirmation_acceptance_test.rb +24 -0
  272. data/test/integration/checkout/paypal_acceptance_test.rb +34 -0
  273. data/test/integration/checkout/shipping_address_acceptance_test.rb +91 -0
  274. data/test/integration/checkout/shipping_method_acceptance_test.rb +51 -0
  275. data/test/integration/homepage_acceptance_test.rb +24 -0
  276. data/test/integration/products_acceptance_test.rb +12 -0
  277. data/test/integration/static_pages_acceptance_test.rb +13 -0
  278. data/test/mailers/mailer_test.rb +25 -0
  279. data/test/rails_app/Gemfile +27 -0
  280. data/test/rails_app/Gemfile.lock +218 -0
  281. data/test/rails_app/Rakefile +10 -0
  282. data/test/rails_app/app/controllers/application_controller.rb +6 -0
  283. data/test/rails_app/app/helpers/application_helper.rb +3 -0
  284. data/test/rails_app/app/views/layouts/application.html.erb +10 -0
  285. data/test/rails_app/config/application.rb +25 -0
  286. data/test/rails_app/config/application.yml +61 -0
  287. data/test/rails_app/config/boot.rb +5 -0
  288. data/test/rails_app/config/database.yml +18 -0
  289. data/test/rails_app/config/environment.rb +5 -0
  290. data/test/rails_app/config/environments/development.rb +18 -0
  291. data/test/rails_app/config/environments/production.rb +33 -0
  292. data/test/rails_app/config/environments/test.rb +33 -0
  293. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  294. data/test/rails_app/config/initializers/inflections.rb +2 -0
  295. data/test/rails_app/config/initializers/secret_token.rb +2 -0
  296. data/test/rails_app/config/routes.rb +10 -0
  297. data/test/rails_app/config.ru +4 -0
  298. data/test/rails_app/db/development.sqlite3 +0 -0
  299. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +74 -0
  300. data/test/rails_app/db/schema.rb +52 -0
  301. data/test/rails_app/log/development.log +94 -0
  302. data/test/rails_app/log/test.log +0 -0
  303. data/test/rails_app/public/404.html +26 -0
  304. data/test/rails_app/public/422.html +26 -0
  305. data/test/rails_app/public/500.html +26 -0
  306. data/test/rails_app/public/favicon.ico +0 -0
  307. data/test/rails_app/script/rails +10 -0
  308. data/test/support/address_test_helper.rb +24 -0
  309. data/test/support/checkout_test_helper.rb +40 -0
  310. data/test/support/dbify_sql_helper.rb +12 -0
  311. data/test/support/images/cookware.jpg +0 -0
  312. data/test/support/must_be_like.rb +24 -0
  313. data/test/support/shipping_method_test_helper.rb +5 -0
  314. data/test/support/wait_for_ajax.rb +4 -0
  315. data/test/test_helper.rb +65 -0
  316. data/test/unit/address_test.rb +33 -0
  317. data/test/unit/country_shipping_zone_test.rb +17 -0
  318. data/test/unit/creditcard_test.rb +33 -0
  319. data/test/unit/custom_field_answer/field_test.rb +93 -0
  320. data/test/unit/order_test.rb +70 -0
  321. data/test/unit/payment_method/authorizedotnet_test.rb +21 -0
  322. data/test/unit/payment_method/paypalwp_test.rb +17 -0
  323. data/test/unit/payment_method/splitable_test.rb +18 -0
  324. data/test/unit/picture_test.rb +24 -0
  325. data/test/unit/processor/authorize_net_test.rb +236 -0
  326. data/test/unit/processor/paypal_test.rb +40 -0
  327. data/test/unit/processor/splitable_test.rb +123 -0
  328. data/test/unit/product_group_condition_test.rb +188 -0
  329. data/test/unit/product_group_test.rb +109 -0
  330. data/test/unit/product_test.rb +60 -0
  331. data/test/unit/shipping_cost_calculator_test.rb +19 -0
  332. data/test/unit/shipping_method_test.rb +197 -0
  333. data/test/unit/shop_test.rb +11 -0
  334. data/test/unit/tax_calculator_test.rb +13 -0
  335. data/test/vcr_cassettes/authorize_net/authorize-failure.yml +38 -0
  336. data/test/vcr_cassettes/authorize_net/authorize-success.yml +38 -0
  337. data/test/vcr_cassettes/authorize_net/capture-failure.yml +38 -0
  338. data/test/vcr_cassettes/authorize_net/capture-success.yml +38 -0
  339. data/test/vcr_cassettes/authorize_net/purchase-failure.yml +38 -0
  340. data/test/vcr_cassettes/authorize_net/purchase-success.yml +38 -0
  341. data/test/vcr_cassettes/authorize_net/refund-failure.yml +38 -0
  342. data/test/vcr_cassettes/authorize_net/refund-success.yml +38 -0
  343. data/test/vcr_cassettes/authorize_net/void-authorize.yml +38 -0
  344. data/test/vcr_cassettes/authorize_net/void-failure.yml +38 -0
  345. data/test/vcr_cassettes/authorize_net/void-success.yml +38 -0
  346. data/test/vcr_cassettes/splitable/split-create-failure.yml +48 -0
  347. data/test/vcr_cassettes/splitable/split-create-success.yml +50 -0
  348. data/test/vcr_cassettes/splitable/split-draft-create-failure.yml +50 -0
  349. data/test/vcr_cassettes/splitable/split-draft-create-success.yml +50 -0
  350. data/vendor/assets/javascripts/accounting.js +406 -0
  351. data/vendor/assets/javascripts/autoresize.js +275 -0
  352. data/vendor/assets/javascripts/jquery-1.7.1.js +9267 -0
  353. data/vendor/assets/javascripts/jquery-form.js +980 -0
  354. data/vendor/assets/javascripts/jquery-json.js +193 -0
  355. data/vendor/assets/javascripts/jquery-ui-1.8.16.js +11768 -0
  356. data/vendor/assets/javascripts/jquery-ujs.js +374 -0
  357. data/vendor/assets/javascripts/mustache.js +436 -0
  358. data/vendor/assets/javascripts/nested_form.js +51 -0
  359. metadata +740 -0
@@ -0,0 +1,36 @@
1
+ class CountryShippingZone < ShippingZone
2
+
3
+ has_many :regional_shipping_zones, dependent: :destroy
4
+
5
+ validates :country_code, presence: true
6
+ validate :code_validity
7
+
8
+ before_save :set_name, if: :country_code_changed?
9
+ after_create :create_regions
10
+
11
+ def country
12
+ Carmen::Country.coded(country_code)
13
+ end
14
+
15
+ def self.all_country_codes
16
+ CountryShippingZone.pluck(:country_code)
17
+ end
18
+
19
+ private
20
+
21
+ def code_validity
22
+ unless country
23
+ errors.add(:country_code, "is invalid")
24
+ end
25
+ end
26
+
27
+ def create_regions
28
+ country.subregions.each do | region |
29
+ regional_shipping_zones.create({ state_code: region.code, country_code: country_code })
30
+ end
31
+ end
32
+
33
+ def set_name
34
+ self.name = country.name
35
+ end
36
+ end
@@ -0,0 +1,95 @@
1
+ class Creditcard
2
+ extend ActiveModel::Naming
3
+ include ActiveModel::Conversion
4
+ include ActiveModel::Validations
5
+ extend ActiveModel::Callbacks
6
+ include ActiveModel::Validations::Callbacks
7
+
8
+ attr_accessor :cvv, :expires_on, :number, :first_name, :last_name, :address1, :address2,
9
+ :cardtype, :month, :year, :state, :zipcode
10
+
11
+ alias :verification_value :cvv # ActiveMerchant needs this
12
+
13
+ delegate :display_number, to: :active_merchant_creditcard
14
+
15
+ before_validation :strip_non_numeric_values, if: :number
16
+
17
+ validates_presence_of :last_name, :first_name
18
+
19
+ validates_presence_of :number, message: "^Please enter credit card number"
20
+ validates_numericality_of :number, message: "^Please check the credit card number you entered"
21
+ validates_presence_of :cvv, message: "^Please enter CVV"
22
+
23
+ validate :validation_of_cardtype, if: lambda { |r| r.errors.empty? }
24
+ validate :validation_by_active_merchant, if: lambda { |r| r.errors.empty? }
25
+
26
+ def initialize(attrs = {})
27
+ sanitize_month_and_year(attrs)
28
+
29
+ attrs.each do | name, value |
30
+ send("#{name}=", value)
31
+ end
32
+ end
33
+
34
+ def verification_value?
35
+ true # ActiveMerchant needs this
36
+ end
37
+
38
+ def persisted?
39
+ false
40
+ end
41
+
42
+ private
43
+
44
+ def add_user_data(options = {})
45
+ options[:first_name] = first_name
46
+ options[:last_name] = last_name
47
+ end
48
+
49
+ def add_credit_card_data(options = {})
50
+ options[:year] = year
51
+ options[:month] = month
52
+ options[:number] = number
53
+ options[:type] = cardtype
54
+ options[:verification_value] = verification_value
55
+ end
56
+
57
+ def strip_non_numeric_values
58
+ self.number = number.to_s.gsub('-', '').strip
59
+ end
60
+
61
+ def validation_of_cardtype
62
+ if cardtype = ActiveMerchant::Billing::CreditCard.type?(number)
63
+ self.cardtype = cardtype
64
+ else
65
+ errors.add(:base, 'Please check credit card number. It does not seem right.')
66
+ end
67
+ end
68
+
69
+ def validation_by_active_merchant
70
+ amcard = active_merchant_creditcard
71
+
72
+ unless amcard.valid?
73
+ amcard.errors.full_messages.each { |message| errors.add(:base, message) }
74
+ end
75
+
76
+ amcard.errors.any?
77
+ end
78
+
79
+ def active_merchant_creditcard
80
+ options = {}
81
+ add_user_data(options)
82
+ add_credit_card_data(options)
83
+
84
+ ActiveMerchant::Billing::CreditCard.new(options)
85
+ end
86
+
87
+ def sanitize_month_and_year(attrs)
88
+ month = attrs.delete("expires_on(2i)")
89
+ year = attrs.delete("expires_on(1i)")
90
+ day = attrs.delete("expires_on(3i)")
91
+ attrs.reverse_merge!(month: month)
92
+ attrs.reverse_merge!(year: year)
93
+ end
94
+
95
+ end
@@ -0,0 +1,8 @@
1
+ class CustomField < ActiveRecord::Base
2
+
3
+ has_many :custom_field_answers, dependent: :destroy
4
+
5
+ validates :field_type, presence: true, inclusion: { in: %w(text number date) }
6
+
7
+ validates :name, presence: true
8
+ end
@@ -0,0 +1,101 @@
1
+ class CustomFieldAnswer
2
+ module Field
3
+ extend ActiveSupport::Concern
4
+
5
+ def custom_field=(custom_field)
6
+ set_custom_field(custom_field)
7
+
8
+ extend_module
9
+ end
10
+
11
+ # when question_id is assigned extend the module
12
+ def custom_field_id=(question_id)
13
+ super
14
+
15
+ extend_module
16
+ end
17
+
18
+ # Override Answer initialize method to extend proper Response module
19
+ # For example
20
+ # Answer instance whose question is TextFieldQuestion will extend TextFieldResponse
21
+ def initialize(attributes = nil, options = {})
22
+ super
23
+
24
+ extend_module
25
+ end
26
+
27
+ # We need to override after_initialize module to force instance to extend proper module
28
+ # ActiveRecord instances are created sometimes using malloc which will not invoke the initialize
29
+ # but they will call after_initialize
30
+ def after_initialize
31
+ extend_module
32
+ end
33
+
34
+ def extend_module
35
+ if target_module = module_to_extend
36
+ # Don't include module twice
37
+ kind_of?(target_module) || extend(target_module)
38
+
39
+ # Force value coersion for extended module
40
+ #
41
+ # For example
42
+ # if answer is created as shown below
43
+ # case(1)
44
+ # q = NumberQuestion.new
45
+ # a = Answer.new(:question => q, :value => 23.45)
46
+ # a.float_value ==> nil
47
+ # a.value ==> '23.45'
48
+ #
49
+ # case(2)
50
+ # a = Answer.new(:question => q)
51
+ # a.value = 23.45
52
+ # a.float_value ==> 23.45
53
+ # a.value ==> '23.45'
54
+ #
55
+ # Above case appears bcoz modules are extended after
56
+ # attributes are assigned. reassign_value method will
57
+ # give the modules to fix this problem please refer
58
+ # to NumberResponse reassign_value
59
+ #
60
+ # Note: currently both cases will work as expected
61
+ reassign_value
62
+ end
63
+ end
64
+
65
+ # This method will determine which module to extend
66
+ # Answer instances which are created using select might throw exception
67
+ # For example Answer.find(:conditions => [], :select => "value")
68
+ # retrieve_module_name will throw exception when it tries to access question field
69
+ # This is the only case Answer will fail to extend proper module
70
+ def module_to_extend
71
+ begin
72
+ CustomFieldAnswer::Field.const_get("#{custom_field.field_type.camelize}Field")
73
+ rescue
74
+ nil
75
+ end
76
+ end
77
+
78
+ def reassign_value
79
+ self.value = self.value
80
+ end
81
+
82
+ module DateField
83
+ def value=(value)
84
+ super
85
+
86
+ self.datetime_value = value
87
+ end
88
+ end
89
+
90
+ module NumberField
91
+ def value=(value)
92
+ super
93
+
94
+ self.number_value = value
95
+ end
96
+ end
97
+
98
+ module TextAnswerField
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,17 @@
1
+ class CustomFieldAnswer < ActiveRecord::Base
2
+
3
+ belongs_to :product
4
+
5
+ belongs_to :custom_field
6
+ alias_method :set_custom_field, :custom_field=
7
+
8
+ # this module must be included only after belong_to :custom_field definition
9
+ # this module overrides the dynamic methods which are added by belongs_to
10
+ include Field
11
+
12
+ def custom_field=(custom_field)
13
+ set_custom_field(custom_field)
14
+
15
+ extend_module
16
+ end
17
+ end
@@ -0,0 +1,48 @@
1
+ class LineItem < ActiveRecord::Base
2
+
3
+ # this is used only to pull the picture of the line_item. If after an order is created a product
4
+ # is deleted then that product and all the pictures are gone. In that case line item should
5
+ # display image not available. Besides image line_item should not pull any data from product and
6
+ # instead used the attributes copied from product to line_item
7
+ belongs_to :product
8
+
9
+ store :metadata, accessors: [ :picture_tiny, :picture_small, :picture_medium, :picture_large,
10
+ :picture_tiny_plus, :picture_small_plus, :picture_medium_plus, :picture_large_plus,
11
+ :product_permalink ]
12
+
13
+ belongs_to :order
14
+
15
+ validates_presence_of :order_id
16
+ validates_presence_of :product_id
17
+ validates_numericality_of :quantity, minimum: 1
18
+
19
+ before_create :copy_product_attributes
20
+
21
+ alias_attribute :name, :product_name
22
+ alias_attribute :description, :product_description
23
+
24
+ def price
25
+ product_price * quantity
26
+ end
27
+
28
+ # updates quantity count. If count is zero then deletes the line_item .
29
+ def update_quantity(count)
30
+ count = count.to_i
31
+ (count > 0) ? update_attributes(quantity: count) : destroy
32
+ end
33
+
34
+ private
35
+
36
+ def copy_product_attributes
37
+ self.product_name = product.name
38
+ self.product_description = product.description
39
+ self.product_price = product.price
40
+ self.product_permalink = product.permalink
41
+
42
+ if product.picture
43
+ %w(tiny tiny_plus small small_plus medium medium_plus large large_plus).each do |size|
44
+ send("picture_#{size}=", product.picture.picture_url(size.intern))
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ class Link < ActiveRecord::Base
2
+ validates_presence_of :name, :url
3
+ end
@@ -0,0 +1,8 @@
1
+ class LinkGroup < ActiveRecord::Base
2
+
3
+ include Permalink::Builder
4
+
5
+ has_many :navigations, dependent: :destroy
6
+
7
+ validates :name, presence: true, uniqueness: { case_sensitive: false }
8
+ end
@@ -0,0 +1,7 @@
1
+ class Navigation < ActiveRecord::Base
2
+ belongs_to :link_group
3
+ belongs_to :product_group
4
+
5
+ validates :link_group, presence: true
6
+ validates :product_group, presence: true
7
+ end
@@ -0,0 +1,158 @@
1
+ class Order < ActiveRecord::Base
2
+
3
+ # to allow extensions to store any type of information
4
+ store :metadata
5
+
6
+ attr_accessor :validate_email, :billing_address_same_as_shipping
7
+ attr_protected :number
8
+
9
+ belongs_to :user
10
+ belongs_to :payment_method
11
+ belongs_to :shipping_method
12
+
13
+ has_one :shipping_address, dependent: :destroy
14
+ has_one :billing_address, dependent: :destroy
15
+
16
+ has_many :shipments
17
+ has_many :line_items, dependent: :destroy
18
+ has_many :products, through: :line_items
19
+ has_many :payment_transactions, dependent: :destroy
20
+
21
+ accepts_nested_attributes_for :shipping_address, allow_destroy: true
22
+ accepts_nested_attributes_for :billing_address, reject_if: :billing_address_same_as_shipping? , allow_destroy: true
23
+
24
+ delegate :tax, to: :tax_calculator
25
+ delegate :shipping_cost, to: :shipping_cost_calculator
26
+
27
+ validates :email, email: true, if: :validate_email
28
+
29
+ validates_inclusion_of :shipping_status, in: %W( nothing_to_ship shipped shipping_pending cancelled )
30
+ validates_inclusion_of :status, in: %W( open closed )
31
+ validates_inclusion_of :checkout_status, in: %W( items_added_to_cart billing_address_provided shipping_method_provided )
32
+
33
+ before_create :set_order_number
34
+
35
+ # Look at order_observer to see all the callbacks.
36
+ #
37
+ # capture is a method defined on kernel by Rails hence kapture is used .
38
+ #
39
+ # An order goes into pending state when it is paid for using 'Cash on delivery'
40
+ state_machine :payment_status, initial: :abandoned do
41
+
42
+ event(:authorize) { transition abandoned: :authorized }
43
+ event(:pending) { transition abandoned: :pending }
44
+ event(:kapture ) { transition authorized: :purchased }
45
+ event(:refund) { transition purchased: :refunded }
46
+
47
+ event(:purchase) { transition [:abandoned, :pending] => :purchased }
48
+ event(:void) { transition [:authorized, :pending] => :voided }
49
+
50
+ state all - [ :abandoned ] do
51
+ validates :payment_method, presence: true
52
+ end
53
+ end
54
+
55
+ state_machine :shipping_status, initial: :nothing_to_ship do
56
+ after_transition on: :shipped, do: :after_shipped
57
+
58
+ event(:shipping_pending) { transition nothing_to_ship: :shipping_pending }
59
+ event(:shipped) { transition shipping_pending: :shipped }
60
+ event(:cancel_shipment) { transition shipping_pending: :nothing_to_ship }
61
+ end
62
+
63
+ def mark_as_purchased!
64
+ touch(:purchased_at) unless purchased_at
65
+ end
66
+
67
+ def available_shipping_methods
68
+ ShippingMethod.available_for(line_items_total, shipping_address)
69
+ end
70
+
71
+ def item_count
72
+ line_items.count
73
+ end
74
+
75
+ def add(product)
76
+ options = { product_id: product.id }
77
+ if line_items.where(options).empty?
78
+ line_items.create(options.merge(quantity: 1))
79
+ end
80
+ end
81
+
82
+ def update_quantity(data = {})
83
+ data.each do |product_id, quantity|
84
+ if line_item = line_item_for(product_id)
85
+ line_item.update_quantity(quantity)
86
+ end
87
+ end
88
+ end
89
+
90
+ def remove(product)
91
+ update_quantity({product.id => 0})
92
+ end
93
+
94
+ # Returns the total price of all line items in float.
95
+ def line_items_total
96
+ line_items.reduce(BigDecimal('0')) do |sum, line_item|
97
+ sum + BigDecimal(line_item.price.to_s)
98
+ end.round(2).to_f
99
+ end
100
+
101
+ def total_amount
102
+ line_items_total + shipping_cost + tax
103
+ end
104
+
105
+ def total_amount_in_cents
106
+ Util.in_cents(total_amount)
107
+ end
108
+
109
+ def to_param
110
+ number
111
+ end
112
+
113
+ def final_billing_address
114
+ (shipping_address && !shipping_address.use_for_billing) ? billing_address : shipping_address
115
+ end
116
+
117
+ def initialize_addresses
118
+ shipping_address || build_shipping_address(country_code: "US", use_for_billing: true)
119
+ billing_address || build_billing_address(country_code: "US", use_for_billing: false)
120
+ end
121
+
122
+ def billing_address_same_as_shipping?
123
+ billing_address_same_as_shipping
124
+ end
125
+
126
+ def line_item_for(product_id)
127
+ line_items.find_by_product_id(product_id)
128
+ end
129
+
130
+ def shippable_countries
131
+ ShippingMethod.available_for_countries(line_items_total)
132
+ end
133
+
134
+ private
135
+
136
+ def set_order_number
137
+ num = Random.new.rand(11111111...99999999).to_s
138
+ while self.class.exists?(number: num) do
139
+ num = Random.new.rand(11111111...99999999).to_s
140
+ end
141
+
142
+ self.number = num
143
+ end
144
+
145
+ def tax_calculator
146
+ @_tax_calculator ||= TaxCalculator.new(self)
147
+ end
148
+
149
+ def shipping_cost_calculator
150
+ @_shipping_cost_calculator ||= ShippingCostCalculator.new(self)
151
+ end
152
+
153
+ def after_shipped
154
+ Mailer.delay.shipment_notification_to_buyer(number)
155
+ touch(:shipped_at)
156
+ end
157
+
158
+ end
@@ -0,0 +1,20 @@
1
+ class PaymentMethod < ActiveRecord::Base
2
+
3
+ store :metadata
4
+
5
+ include Permalink::Builder
6
+
7
+ # By default payment_method does not require that application must use SSL.
8
+ # Individual payment method should override this method.
9
+ def use_ssl?
10
+ false
11
+ end
12
+
13
+ def demodulized_underscore
14
+ self.class.name.demodulize.underscore
15
+ end
16
+
17
+ def self.partialize
18
+ name.gsub("PaymentMethod::","").underscore
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ class PaymentTransaction < ActiveRecord::Base
2
+
3
+ belongs_to :order
4
+ serialize :params, Hash
5
+ serialize :metadata, Hash
6
+
7
+ def to_partial_path
8
+ "admin/orders/payment_transactions/#{order.payment_method.class.partialize}"
9
+ end
10
+
11
+ end
@@ -0,0 +1,30 @@
1
+ class Picture < ActiveRecord::Base
2
+
3
+ belongs_to :product
4
+
5
+ mount_uploader :picture, PictureUploader
6
+
7
+ validates_presence_of :picture
8
+
9
+ before_save :update_picture_attributes
10
+
11
+ %w(tiny tiny_plus small small_plus medium medium_plus large large_plus).each do |version|
12
+ define_method :"#{version}_height" do
13
+ picture.send(version).height
14
+ end
15
+
16
+ define_method :"#{version}_width" do
17
+ picture.send(version).width
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def update_picture_attributes
24
+ if picture.present?
25
+ self.content_type = picture.file.content_type
26
+ self.file_size = picture.file.size
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,76 @@
1
+ class Product < ActiveRecord::Base
2
+
3
+ include Permalink::Builder
4
+
5
+ alias_attribute :title, :name
6
+
7
+ attr_accessor :pictures_order
8
+
9
+ validates :status, inclusion: { in: %w(active hidden sold_out) }, presence: true
10
+
11
+ has_many :pictures, order: 'pictures.position'
12
+
13
+ has_many :custom_field_answers, dependent: :destroy
14
+
15
+ accepts_nested_attributes_for :pictures, allow_destroy: true, reject_if: proc { |r| r[:picture].blank? }
16
+
17
+ accepts_nested_attributes_for :custom_field_answers, allow_destroy: true
18
+
19
+ after_initialize :initialize_status
20
+
21
+ scope :with_prictures, includes: 'pictures'
22
+
23
+ scope :active, where(status: 'active')
24
+ scope :hidden, where(status: 'hidden')
25
+ scope :sold_out, where(status: 'sold_out')
26
+
27
+
28
+ validates_presence_of :name, :description, :price
29
+
30
+ validates_numericality_of :price
31
+
32
+ def picture
33
+ pictures.first
34
+ end
35
+
36
+ # path is full path to the picture.
37
+ #
38
+ # Rails.root.join('db', 'original_pictures', filename )
39
+ #
40
+ # This method is used only in development and test to attach a picture.
41
+ def attach_picture(filename, path)
42
+ img = File.open(path) {|i| i.read}
43
+ encoded_img = Base64.encode64 img
44
+ io = FilelessIO.new(Base64.decode64(encoded_img))
45
+ io.original_filename = filename
46
+ p = Picture.new(product: self, picture: io)
47
+ p.save
48
+ end
49
+
50
+ def find_or_build_answer_for_field(field)
51
+ custom_field_answers.detect {|t| t.custom_field_id.to_s == field.id.to_s } ||
52
+ custom_field_answers.build(custom_field_id: field.id)
53
+ end
54
+
55
+ def find_or_build_all_answers
56
+ CustomField.all.each { |f| find_or_build_answer_for_field(f) }
57
+ end
58
+
59
+
60
+ def pictures_order=(value)
61
+ return if value.empty?
62
+ ordered_pictures = ActiveSupport::JSON.decode(value)
63
+ current_pictures = pictures
64
+
65
+ ordered_pictures.each{|position, picture_id|
66
+ if picture_id.present?
67
+ pic = current_pictures.find { |x| x.id == picture_id.to_i }
68
+ pic.update_column(:position, position) if pic
69
+ end
70
+ }
71
+ end
72
+
73
+ def initialize_status
74
+ self.status ||= 'active'
75
+ end
76
+ end
@@ -0,0 +1,65 @@
1
+ class ProductGroup < ActiveRecord::Base
2
+
3
+ include Permalink::Builder
4
+
5
+ validates :name, presence: true
6
+
7
+ has_many :product_group_conditions, dependent: :destroy, extend: ProductGroupSearch::Extension
8
+
9
+ has_many :navigations, dependent: :destroy
10
+
11
+ has_many :link_groups, through: :navigations
12
+
13
+ accepts_nested_attributes_for :product_group_conditions, allow_destroy: true
14
+
15
+ # determines if the given product exists in the product group
16
+ def contains?(product)
17
+ products.include?(product)
18
+ end
19
+
20
+ def self.fields
21
+ c = []
22
+ c << { "id" => 'name', "name" => 'Name', "field_type" => 'text' }
23
+ c << { "id" => 'price', "name" => 'Price', "field_type" => 'number' }
24
+
25
+ CustomField.all.map do | field |
26
+ c << field.attributes.slice("id", "name", "field_type")
27
+ end
28
+
29
+ c
30
+ end
31
+
32
+ def self.operators
33
+ {
34
+ text: [{ name: 'Contains', value: 'contains' },
35
+ { name: 'Starts With', value: 'starts'},
36
+ { name: 'Ends With', value:'ends'}],
37
+
38
+ number: [{ name: 'Equal', value: 'eq' },
39
+ { name: 'Greater Than', value: 'gt' },
40
+ { name: 'Grater Than Equal To', value:'gteq' },
41
+ { name: 'Less Than', value: 'lt' },
42
+ { name: 'Less Than Equal To', value: 'lteq'}],
43
+
44
+ date: [{ name: 'On', value: 'eq' },
45
+ { name: 'After', value: 'gt' },
46
+ { name: 'On or After', value:'gteq' },
47
+ { name: 'Before', value: 'lt' },
48
+ { name: 'On or Before', value: 'lteq'}],
49
+ }
50
+ end
51
+
52
+ def products
53
+ product_group_conditions.search
54
+ end
55
+
56
+ def summarize
57
+ product_group_conditions.map(&:summary).join(' and ')
58
+ end
59
+
60
+ # list of all product groups containing input product
61
+ def self.contains_product(product)
62
+ all.select { | product_group | product_group.contains?(product) }
63
+ end
64
+
65
+ end