solidus_invoice 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +1 -0
- data/.rubocop.yml +250 -0
- data/.rvmrc +1 -0
- data/.travis.yml +18 -0
- data/Gemfile +10 -0
- data/MIT-LICENSE +20 -0
- data/README.md +38 -0
- data/Rakefile +32 -0
- data/app/assets/javascripts/spree/backend/solidus_invoice.js +2 -0
- data/app/assets/javascripts/spree/frontend/solidus_invoice.js +2 -0
- data/app/assets/stylesheets/spree/backend/solidus_invoice.scss +4 -0
- data/app/assets/stylesheets/spree/frontend/solidus_invoice.css +4 -0
- data/app/jobs/application_job.rb +9 -0
- data/app/jobs/solidus_invoice/daily_summary_job.rb +32 -0
- data/app/jobs/solidus_invoice/invoice_job.rb +22 -0
- data/app/models/concerns/solidus_invoice/line_item_concern.rb +11 -0
- data/app/models/concerns/solidus_invoice/order_concern.rb +44 -0
- data/app/models/concerns/solidus_invoice/shipment_concern.rb +11 -0
- data/app/models/concerns/solidus_invoice/store_concern.rb +11 -0
- data/app/models/spree.rb +7 -0
- data/app/models/spree/invoice.rb +61 -0
- data/app/models/spree/invoice_serial.rb +9 -0
- data/app/overrides/spree/line_item_override.rb +5 -0
- data/app/overrides/spree/order_override.rb +5 -0
- data/app/overrides/spree/shipment_override.rb +5 -0
- data/app/overrides/spree/store_override.rb +5 -0
- data/bin/rails +9 -0
- data/bin/test +7 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20191107042751_create_spree_invoices.rb +14 -0
- data/db/migrate/20191115204118_create_spree_invoice_serials.rb +13 -0
- data/db/migrate/20191205205004_add_tax_uid_to_spree_addresses.rb +7 -0
- data/db/migrate/20191223173229_add_preferences_to_spree_invoices.rb +7 -0
- data/lib/generators/solidus_invoice/install/install_generator.rb +22 -0
- data/lib/solidus_invoice.rb +4 -0
- data/lib/solidus_invoice/engine.rb +22 -0
- data/lib/solidus_invoice/errors/invalid_serial_error.rb +6 -0
- data/lib/solidus_invoice/factories.rb +34 -0
- data/lib/solidus_invoice/railtie.rb +6 -0
- data/lib/solidus_invoice/version.rb +5 -0
- data/lib/tasks/solidus_invoice_tasks.rake +5 -0
- data/solidus_invoice.gemspec +37 -0
- data/spec/jobs/solidus_invoice/daily_summary_job_spec.rb +24 -0
- data/spec/jobs/solidus_invoice/invoice_job_spec.rb +76 -0
- data/spec/models/spree/address_spec.rb +11 -0
- data/spec/models/spree/invoice_serial_spec.rb +16 -0
- data/spec/models/spree/invoice_spec.rb +55 -0
- data/spec/models/spree/line_item_spec.rb +37 -0
- data/spec/models/spree/order_spec.rb +31 -0
- data/spec/models/spree/shipment_spec.rb +26 -0
- data/spec/models/spree/store_spec.rb +9 -0
- data/spec/spec_helper.rb +98 -0
- metadata +320 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sunat_invoice'
|
4
|
+
|
5
|
+
class SolidusInvoice::InvoiceJob < ApplicationJob
|
6
|
+
queue_as :default
|
7
|
+
|
8
|
+
def perform(*args)
|
9
|
+
opts = args[0]
|
10
|
+
invoice = Spree::Invoice.find(opts[:invoice_id])
|
11
|
+
env = opts[:env]
|
12
|
+
|
13
|
+
sunat_invoice = invoice.build_sunat_invoice
|
14
|
+
|
15
|
+
if invoice.doc_type == '01'
|
16
|
+
# send to SUNAT
|
17
|
+
client = SunatInvoice::InvoiceClient.new(env)
|
18
|
+
response = client.dispatch(sunat_invoice)
|
19
|
+
invoice.update(communicated: true) if response.success?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'solidus_invoice/errors/invalid_serial_error'
|
4
|
+
|
5
|
+
module SolidusInvoice
|
6
|
+
module OrderConcern
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
has_many :invoices
|
11
|
+
after_save :generate_invoice
|
12
|
+
|
13
|
+
def generate_invoice
|
14
|
+
return unless can_generate_invoice?
|
15
|
+
|
16
|
+
doc_type = bill_address.tax_uid? ? '01' : '03'
|
17
|
+
doc_number = next_correlative(doc_type, store.id)
|
18
|
+
|
19
|
+
# generate Spree::Invoice
|
20
|
+
invoices.create(invoice_serial: get_serial(doc_type, store),
|
21
|
+
doc_number: doc_number,
|
22
|
+
order: self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def can_generate_invoice?
|
26
|
+
completed? && paid?
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_serial(doc_type, store = Spree::Store.default)
|
30
|
+
store.invoice_serials.find_by(doc_type: doc_type)
|
31
|
+
end
|
32
|
+
|
33
|
+
# given a store and document type get next posible correlative
|
34
|
+
def next_correlative(doc_type, store_id = Spree::Store.default.id)
|
35
|
+
store = Spree::Store.includes(invoice_serials: :invoices).find(store_id)
|
36
|
+
serial = get_serial(doc_type, store)
|
37
|
+
raise SolidusInvoice::InvalidSerialError unless serial
|
38
|
+
|
39
|
+
current = serial&.invoices&.order(doc_number: :desc)&.first&.doc_number || 0
|
40
|
+
current + 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/app/models/spree.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sunat_invoice'
|
4
|
+
|
5
|
+
class Spree::Invoice < Spree::Base
|
6
|
+
validates :doc_number, presence: true
|
7
|
+
|
8
|
+
belongs_to :order
|
9
|
+
belongs_to :invoice_serial
|
10
|
+
|
11
|
+
delegate :doc_type, :serial, to: :invoice_serial
|
12
|
+
|
13
|
+
preference :sunat_attributes, :hash
|
14
|
+
|
15
|
+
def sunat_attributes
|
16
|
+
attrs = (preferred_sunat_attributes || {}).dup
|
17
|
+
provider = SunatInvoice::Provider.new(attrs[:provider] || {})
|
18
|
+
attrs[:provider] = provider
|
19
|
+
attrs
|
20
|
+
end
|
21
|
+
|
22
|
+
# return a `SunatInvoice::Invoice`
|
23
|
+
def build_sunat_invoice
|
24
|
+
# generate SunatInvoice::Invoice
|
25
|
+
sunat_invoice = SunatInvoice::Invoice.new(sunat_attributes)
|
26
|
+
|
27
|
+
# add invoice lines from line_items
|
28
|
+
order.line_items.map do |line_item|
|
29
|
+
add_sunat_line(sunat_invoice, line_item)
|
30
|
+
end
|
31
|
+
|
32
|
+
# add invoice lines from shipment
|
33
|
+
order.shipments.map do |shipment|
|
34
|
+
next unless shipment.amount.positive?
|
35
|
+
|
36
|
+
add_sunat_line(sunat_invoice, shipment)
|
37
|
+
end
|
38
|
+
|
39
|
+
# store xml
|
40
|
+
store_xml(sunat_invoice.xml) if respond_to?(:store_xml)
|
41
|
+
|
42
|
+
sunat_invoice
|
43
|
+
end
|
44
|
+
|
45
|
+
# add a new `SunatInvoice::Item` to the given sunat invoice
|
46
|
+
# sunat_invoice => `SunatInvoice::Invoice` instance to add lines
|
47
|
+
# resource => an order's line_item or shipment
|
48
|
+
def add_sunat_line(sunat_invoice, resource)
|
49
|
+
# TODO: handle items with taxes different than vat
|
50
|
+
tax_amount = resource.adjustments.tax.sum(:amount)
|
51
|
+
tax = SunatInvoice::Tax.new(amount: tax_amount, tax_type: :igv)
|
52
|
+
quantity = resource.respond_to?(:quantity) ? resource.quantity : 1
|
53
|
+
item_attrs = {
|
54
|
+
quantity: quantity,
|
55
|
+
price: resource.price_excluding_vat,
|
56
|
+
price_code: '01',
|
57
|
+
taxes: [tax]
|
58
|
+
}
|
59
|
+
sunat_invoice.lines << SunatInvoice::Item.new(item_attrs)
|
60
|
+
end
|
61
|
+
end
|
data/bin/rails
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
4
|
+
|
5
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
6
|
+
ENGINE_PATH = File.expand_path('../../lib/solidus_invoice/engine', __FILE__)
|
7
|
+
|
8
|
+
require 'rails/all'
|
9
|
+
require 'rails/engine/commands'
|
data/bin/test
ADDED
data/config/routes.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateSpreeInvoices < ActiveRecord::Migration[6.0]
|
4
|
+
def change
|
5
|
+
create_table :spree_invoices do |t|
|
6
|
+
t.integer :doc_number
|
7
|
+
t.integer :order_id
|
8
|
+
t.integer :invoice_serial_id
|
9
|
+
t.boolean :communicated, default: false
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateSpreeInvoiceSerials < ActiveRecord::Migration[6.0]
|
4
|
+
def change
|
5
|
+
create_table :spree_invoice_serials do |t|
|
6
|
+
t.string :serial
|
7
|
+
t.integer :store_id
|
8
|
+
t.string :doc_type
|
9
|
+
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusInvoice
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
class_option :auto_run_migrations, type: :boolean, default: false
|
7
|
+
|
8
|
+
def add_migrations
|
9
|
+
run 'bundle exec rake railties:install:migrations FROM=solidus_invoice'
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_migrations
|
13
|
+
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
|
14
|
+
if run_migrations
|
15
|
+
run 'bundle exec rake db:migrate'
|
16
|
+
else
|
17
|
+
puts 'Skipping rake db:migrate, don\'t forget to run it!'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusInvoice
|
4
|
+
class Engine < Rails::Engine
|
5
|
+
require 'spree/core'
|
6
|
+
isolate_namespace Spree
|
7
|
+
engine_name 'solidus_invoice'
|
8
|
+
|
9
|
+
# use rspec for tests
|
10
|
+
config.generators do |g|
|
11
|
+
g.test_framework :rspec
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.activate
|
15
|
+
Dir.glob(File.join(File.dirname(__FILE__), '../../app/overrides/**/*_override*.rb')) do |c|
|
16
|
+
require_dependency(c)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
config.to_prepare(&method(:activate).to_proc)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
FactoryBot.define do
|
4
|
+
factory :invoice, class: 'Spree::Invoice' do
|
5
|
+
doc_number { rand(450) }
|
6
|
+
order
|
7
|
+
invoice_serial
|
8
|
+
|
9
|
+
factory :invoice_with_serial do
|
10
|
+
before(:create) do |invoice|
|
11
|
+
store = create(:store)
|
12
|
+
invoice.invoice_serial { create(:invoice_serial, store: store) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
factory :invoice_serial, class: 'Spree::InvoiceSerial' do
|
18
|
+
store
|
19
|
+
doc_type { '03' }
|
20
|
+
serial { 'B023' }
|
21
|
+
end
|
22
|
+
|
23
|
+
factory :provider, class: 'SunatInvoice::Provider' do
|
24
|
+
signature_id { 'signatureST' }
|
25
|
+
signature_location_id { 'signQWI3' }
|
26
|
+
ruc { '20100454523' }
|
27
|
+
name { 'MY BUSINESS' }
|
28
|
+
end
|
29
|
+
|
30
|
+
factory :customer, class: 'SunatInvoice::Customer' do
|
31
|
+
ruc { '20293028401' }
|
32
|
+
name { 'SOME BUSINESS' }
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$:.push File.expand_path('lib', __dir__)
|
4
|
+
require 'solidus_invoice/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'solidus_invoice'
|
8
|
+
s.version = SolidusInvoice::VERSION
|
9
|
+
s.summary = 'generate invoice documents for solidus orders'
|
10
|
+
s.description = s.summary
|
11
|
+
s.license = 'MIT'
|
12
|
+
|
13
|
+
s.author = 'César Carruitero'
|
14
|
+
s.email = 'ccarruitero@protonmail.com'
|
15
|
+
s.homepage = 'https://github.com/ccarruitero/solidus_invoice'
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
|
20
|
+
s.add_dependency 'solidus_core'
|
21
|
+
s.add_dependency 'solidus_support'
|
22
|
+
s.add_dependency 'sunat_invoice'
|
23
|
+
|
24
|
+
s.add_development_dependency 'capybara'
|
25
|
+
s.add_development_dependency 'database_cleaner'
|
26
|
+
s.add_development_dependency 'factory_bot'
|
27
|
+
s.add_development_dependency 'ffaker'
|
28
|
+
s.add_development_dependency 'mysql2'
|
29
|
+
s.add_development_dependency 'pg'
|
30
|
+
s.add_development_dependency 'pry'
|
31
|
+
s.add_development_dependency 'rspec-rails'
|
32
|
+
s.add_development_dependency 'rubocop'
|
33
|
+
s.add_development_dependency 'sass-rails'
|
34
|
+
s.add_development_dependency 'shoulda-matchers'
|
35
|
+
s.add_development_dependency 'simplecov'
|
36
|
+
s.add_development_dependency 'sqlite3'
|
37
|
+
end
|