acube-rails 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +93 -0
- data/Rakefile +3 -0
- data/app/models/acube/invoice_record.rb +17 -0
- data/app/models/acube/record.rb +9 -0
- data/config/application.rb +9 -0
- data/config/initializers/zeitwerk.rb +7 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20230624195146_create_acube_invoice_records.rb +46 -0
- data/lib/acube/attribute.rb +35 -0
- data/lib/acube/endpoint/auth.rb +33 -0
- data/lib/acube/endpoint/common_base.rb +16 -0
- data/lib/acube/endpoint/invoices.rb +32 -0
- data/lib/acube/endpoint/it_api_base.rb +20 -0
- data/lib/acube/engine.rb +22 -0
- data/lib/acube/invoicer.rb +116 -0
- data/lib/acube/schema/body.rb +96 -0
- data/lib/acube/schema/document.rb +39 -0
- data/lib/acube/schema/header/customer.rb +49 -0
- data/lib/acube/schema/header/header.rb +36 -0
- data/lib/acube/schema/header/supplier.rb +65 -0
- data/lib/acube/signature_checker.rb +45 -0
- data/lib/acube/support/customer.rb +42 -0
- data/lib/acube/support/supplier.rb +42 -0
- data/lib/acube/support/transaction.rb +42 -0
- data/lib/acube/version.rb +3 -0
- data/lib/acube.rb +84 -0
- data/lib/generators/a_cube/install/install_generator.rb +15 -0
- data/lib/tasks/acube.rake +6 -0
- metadata +109 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: efa4cde8156a62daa4d25ae2da27e6f71be7b5ed36b27f97ad77fc3a0d961e1a
|
|
4
|
+
data.tar.gz: 262a5d9937aff89409b5d5c20093ed63bc81016db16c1eb4909cf3be5a6e6d4f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6c204b4482d57e1acf2be4ed401d5f91e122aea8e541de4f9d40a6061604fb61666871c64a51bf483c8a44a3b8cb29244f83c999aa35dc93094e4407ad8ba6cf
|
|
7
|
+
data.tar.gz: 273f3cf9e669898581b5083ed102344248e5f30b421bf33131edf8684328cd5b8c10ae560e994e39303612abd1c6fb69d0bd357316d8ff31327191b6b8c1412a
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2023 Pietro Moro
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# ACube API - Rails [WIP]
|
|
2
|
+
Wrapper library around the ACube API for ruby on rails. Quickly set up resources to manage invoices.
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
This will copy over the initializer file to your rails application and the necessary migrations:
|
|
6
|
+
```
|
|
7
|
+
$ bin/rails g a_cube:install
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
In the initializer file you have to set at the very least the following:
|
|
11
|
+
```ruby
|
|
12
|
+
config.username = "your-login-email"
|
|
13
|
+
config.password = "your-login-password"
|
|
14
|
+
|
|
15
|
+
config.invoice_endpoint = ENV.fetch('ACUBE_INVOICE_ENDPOINT', "https://api-sandbox.acubeapi.com")
|
|
16
|
+
config.common_endpoint = ENV.fetch('ACUBE_COMMON_ENDPOINT', "https://common-sandbox.api.acubeapi.com")
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
For a complete list of configuration options, see the initializer file.
|
|
20
|
+
You have to designate two models to be used that will serve as the supplier/consumer contacts.
|
|
21
|
+
These will take care of the mapping between your application and the ACube API.
|
|
22
|
+
```ruby
|
|
23
|
+
include ACube::Support::Supplier
|
|
24
|
+
|
|
25
|
+
as_supplier do |s|
|
|
26
|
+
s.first_name = "..."
|
|
27
|
+
s.last_name = :last_name
|
|
28
|
+
end
|
|
29
|
+
```
|
|
30
|
+
String means constant value, symbol means method name on the model that will get called when the invoice is created.
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
include ACube::Support::Consumer
|
|
34
|
+
|
|
35
|
+
as_custoemr do |c|
|
|
36
|
+
c.first_name = "..."
|
|
37
|
+
c.last_name = :last_name
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
For a full list of supported attributes, see the relevant file.
|
|
41
|
+
|
|
42
|
+
The last model is the one that will be associated with the invoices, so the payment model per say.
|
|
43
|
+
```ruby
|
|
44
|
+
class Payment < ApplicationRecord
|
|
45
|
+
has_one_invoice :invoice
|
|
46
|
+
|
|
47
|
+
as_transaction do |t|
|
|
48
|
+
t.amount = :amount
|
|
49
|
+
t.currency = "EUR"
|
|
50
|
+
t.payment_date = :created_at
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
```
|
|
54
|
+
The `has_one_invoice` method will create the association between the payment and the invoice.
|
|
55
|
+
The `as_transaction` method will create the mapping between the payment and the invoice.
|
|
56
|
+
|
|
57
|
+
The last step is to actually publish the invoice to the ACube API.
|
|
58
|
+
```ruby
|
|
59
|
+
# In your controller somewhere
|
|
60
|
+
def create
|
|
61
|
+
@payment = Payment.new(payment_params)
|
|
62
|
+
supplier = Supplier.new(...)
|
|
63
|
+
consumer = Consumer.new(...)
|
|
64
|
+
|
|
65
|
+
if @payment.save
|
|
66
|
+
@payment.publish_invoice(supplier, consumer, :FPR12)
|
|
67
|
+
# ...
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
Add this line to your application's Gemfile:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
gem "acube-rails", require: "acube"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
And then execute:
|
|
80
|
+
```bash
|
|
81
|
+
$ bundle
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or install it yourself as:
|
|
85
|
+
```bash
|
|
86
|
+
$ gem install acube-rails
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Contributing
|
|
90
|
+
Contribution directions go here.
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ACube
|
|
4
|
+
class InvoiceRecord < ACube.invoice_base_class.constantize
|
|
5
|
+
self.table_name = 'acube_invoice_records'
|
|
6
|
+
|
|
7
|
+
belongs_to :record, polymorphic: true, touch: true
|
|
8
|
+
has_one_attached :pdf
|
|
9
|
+
|
|
10
|
+
enum format: %i[ FPA12 FPR12 ]
|
|
11
|
+
enum kind: %i[ TD01 TD02 TD03 TD04 TD05 TD06 TD16 TD17 TD18 TD19 TD20 TD21 TD22 TD23 TD24 TD25 TD26 TD27 TD28 ]
|
|
12
|
+
|
|
13
|
+
enum status: { notification_error: -4, download_error: -3, creation_error: -2, error: -1, created: 0, sent: 1, downloaded: 2, not_received: 3, rejected: 4, delivered: 5 }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
ActiveSupport.run_load_hooks :acube_invoice_record, ACube::InvoiceRecord
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
class CreateACubeInvoiceRecords < ActiveRecord::Migration[7.0]
|
|
2
|
+
def up
|
|
3
|
+
if connection.adapter_name.downcase != 'postgresql'
|
|
4
|
+
raise "This migration is only compatible with PostgreSQL at the moment."
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
primary_key_type, foreign_key_type = primary_and_foreign_key_types
|
|
8
|
+
|
|
9
|
+
create_table :acube_invoice_records, id: primary_key_type do |t|
|
|
10
|
+
t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
|
|
11
|
+
|
|
12
|
+
t.string :name, null: false
|
|
13
|
+
t.string :webhook_uuid
|
|
14
|
+
t.integer :status, null: false, default: 0
|
|
15
|
+
t.integer :format, null: false
|
|
16
|
+
t.integer :kind, null: false
|
|
17
|
+
t.string :progressive, null: false
|
|
18
|
+
t.text :json_body, size: :long
|
|
19
|
+
t.text :xml_body, size: :long
|
|
20
|
+
|
|
21
|
+
t.timestamps
|
|
22
|
+
|
|
23
|
+
t.index [ :record_type, :record_id, :name ], name: "index_acube_rails_acube_invoice_records_uniqueness", unique: true
|
|
24
|
+
t.index :progressive, unique: true
|
|
25
|
+
t.index :webhook_uuid, unique: true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
execute <<-SQL
|
|
29
|
+
CREATE SEQUENCE acube_invoice_records_progressive_seq START 1 INCREMENT 1 OWNED BY acube_invoice_records.progressive;
|
|
30
|
+
SQL
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def down
|
|
34
|
+
drop_table :acube_invoice_records
|
|
35
|
+
execute "DROP SEQUENCE IF EXISTS acube_invoice_records_progressive_seq;"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
def primary_and_foreign_key_types
|
|
40
|
+
config = Rails.configuration.generators
|
|
41
|
+
setting = config.options[config.orm][:primary_key_type]
|
|
42
|
+
primary_key_type = setting || :primary_key
|
|
43
|
+
foreign_key_type = setting || :bigint
|
|
44
|
+
[primary_key_type, foreign_key_type]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
module Attribute
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
class_methods do
|
|
6
|
+
def has_one_invoice(name, strict_loading: strict_loading_by_default)
|
|
7
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
|
8
|
+
def #{name}
|
|
9
|
+
invoice_record_#{name} || build_invoice_record_#{name}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def publish_#{name}!(supplier, customer, format)
|
|
13
|
+
builder = ACube::Invoicer.from(supplier: supplier, customer: customer, invoice: self, format: format)
|
|
14
|
+
builder.create_invoice(self, "#{name}")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def #{name}?
|
|
18
|
+
invoice_record_#{name}.present?
|
|
19
|
+
end
|
|
20
|
+
CODE
|
|
21
|
+
|
|
22
|
+
include ACube::Support::Transaction
|
|
23
|
+
has_one :"invoice_record_#{name}", -> { where(name: name) }, class_name: 'ACube::InvoiceRecord', as: :record, inverse_of: :record, autosave: true, dependent: :destroy, strict_loading: strict_loading
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def with_all_invoice_records
|
|
27
|
+
eager_load(invoice_record_association_names)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def invoice_record_association_names
|
|
31
|
+
reflect_on_all_associations(:has_one).collect(&:name).select { |n| n.start_with?("invoice_record_") }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
module Endpoint
|
|
3
|
+
class Auth < CommonBase
|
|
4
|
+
def login
|
|
5
|
+
response = connection.post do |req|
|
|
6
|
+
req.url "/login"
|
|
7
|
+
req.body = {
|
|
8
|
+
email: ACube.username,
|
|
9
|
+
password: ACube.password
|
|
10
|
+
}.to_json
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
if response.success?
|
|
14
|
+
token = JSON.parse(response.body)["token"]
|
|
15
|
+
Rails.cache.write(ACube.auth_token_cache_key, token, expires_in: 20.hours)
|
|
16
|
+
return token
|
|
17
|
+
else
|
|
18
|
+
raise "Login failed: #{response.body} -- #{response.inspect}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def logout
|
|
23
|
+
Rails.cache.delete(ACube.auth_token_cache_key)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def token!
|
|
27
|
+
Rails.cache.fetch(ACube.auth_token_cache_key) do
|
|
28
|
+
login
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
|
|
3
|
+
module ACube
|
|
4
|
+
module Endpoint
|
|
5
|
+
class CommonBase
|
|
6
|
+
attr_reader :connection
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@connection = Faraday.new(
|
|
10
|
+
url: ACube.common_endpoint,
|
|
11
|
+
headers: {'Content-Type' => 'application/json'}
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
module Endpoint
|
|
3
|
+
class Invoices < ItApiBase
|
|
4
|
+
def create(invoice_xml)
|
|
5
|
+
response = connection.post do |req|
|
|
6
|
+
req.url "/invoices"
|
|
7
|
+
req.body = invoice_xml
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
if response.success?
|
|
11
|
+
return JSON.parse(response.body)["uuid"]
|
|
12
|
+
else
|
|
13
|
+
raise "Invoice creation failed: #{response.body} -- #{response.inspect} "
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def download(uuid)
|
|
18
|
+
response = connection.get do |req|
|
|
19
|
+
req.url "/invoices/#{uuid}"
|
|
20
|
+
req.headers['Content-Type'] = 'application/pdf'
|
|
21
|
+
req.headers['X-PrintTheme'] = ACube.invoice_print_theme || 'standard'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if response.success?
|
|
25
|
+
return StringIO.new(response.body)
|
|
26
|
+
else
|
|
27
|
+
raise "Invoice download failed: #{response.body} -- #{response.inspect}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
|
|
3
|
+
module ACube
|
|
4
|
+
module Endpoint
|
|
5
|
+
class ItApiBase
|
|
6
|
+
attr_reader :connection
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@connection = Faraday.new(
|
|
10
|
+
url: ACube.invoice_endpoint,
|
|
11
|
+
headers: {
|
|
12
|
+
'Content-Type' => 'application/xml',
|
|
13
|
+
'Authorization' => 'Bearer ' + ACube::Endpoint::Auth.new.token!,
|
|
14
|
+
'X-SendAsync' => 'true'
|
|
15
|
+
}
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/acube/engine.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "rails"
|
|
2
|
+
require "active_record/railtie"
|
|
3
|
+
require "action_controller/railtie"
|
|
4
|
+
require "active_storage/engine"
|
|
5
|
+
|
|
6
|
+
require "acube"
|
|
7
|
+
|
|
8
|
+
module ACube
|
|
9
|
+
class Engine < ::Rails::Engine
|
|
10
|
+
isolate_namespace ACube
|
|
11
|
+
config.eager_load_namespaces << ACube
|
|
12
|
+
engine_name "acube"
|
|
13
|
+
|
|
14
|
+
config.autoload_paths << "#{root}/app/models"
|
|
15
|
+
|
|
16
|
+
initializer "acube.attribute" do
|
|
17
|
+
ActiveSupport.on_load(:active_record) do
|
|
18
|
+
include ACube::Attribute
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
class Invoicer
|
|
3
|
+
attr_accessor :supplier, :customer, :invoice
|
|
4
|
+
attr_accessor :header, :document
|
|
5
|
+
|
|
6
|
+
def self.from(supplier:, customer:, invoice:, format: :FPR12)
|
|
7
|
+
raise "Format #{format} not supported" unless ACube::InvoiceRecord.formats.include?(format)
|
|
8
|
+
new(supplier, customer, invoice, format)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create_invoice(invoice_base_record, name)
|
|
12
|
+
progressive_val = ACube::InvoiceRecord.connection.execute("SELECT nextval('acube_invoice_records_progressive_seq') FROM acube_invoice_records_progressive_seq").first["nextval"]
|
|
13
|
+
progressive_string = ACube.progressive_string.call(progressive_val)
|
|
14
|
+
document.fill_with(transmission_format: @format, progressive: progressive_string)
|
|
15
|
+
xml_body = document.to_xml
|
|
16
|
+
|
|
17
|
+
invoice_record = ACube::InvoiceRecord.create!(
|
|
18
|
+
record: invoice_base_record,
|
|
19
|
+
name: name,
|
|
20
|
+
format: @format,
|
|
21
|
+
kind: invoice.document_kind,
|
|
22
|
+
status: :created,
|
|
23
|
+
progressive: progressive_string,
|
|
24
|
+
xml_body: xml_body,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
begin
|
|
28
|
+
uuid = ACube::Endpoint::Invoices.new.create(xml_body)
|
|
29
|
+
invoice_record.update_column(:webhook_uuid, uuid)
|
|
30
|
+
rescue => e
|
|
31
|
+
invoice_record.update_column(:status, :creation_error)
|
|
32
|
+
raise e
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def regenerate_invoice(invoice_record_id, also_send: false)
|
|
37
|
+
invoice_record = ACube::InvoiceRecord.find(invoice_record_id)
|
|
38
|
+
raise "This Invoice was already sent to ACube" if invoice_record.status != "creation_error"
|
|
39
|
+
|
|
40
|
+
document.fill_with(transmission_format: invoice_record.format, progressive: invoice_record.progressive)
|
|
41
|
+
xml_body = document.to_xml
|
|
42
|
+
|
|
43
|
+
invoice_record.update_columns(
|
|
44
|
+
status: :created,
|
|
45
|
+
xml_body: xml_body,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if also_send
|
|
49
|
+
begin
|
|
50
|
+
uuid = ACube::Endpoint::Invoices.new.create(xml_body)
|
|
51
|
+
invoice_record.update_column(:webhook_uuid, uuid)
|
|
52
|
+
rescue => e
|
|
53
|
+
invoice_record.update_column(:status, :creation_error)
|
|
54
|
+
raise e
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.retry_invoice_sending(invoice_record_id)
|
|
60
|
+
invoice_record = ACube::InvoiceRecord.find(invoice_record_id)
|
|
61
|
+
raise "This Invoice was already sent to ACube" if invoice_record.status != "creation_error"
|
|
62
|
+
|
|
63
|
+
begin
|
|
64
|
+
uuid = ACube::Endpoint::Invoices.new.create(invoice_record.xml_body)
|
|
65
|
+
invoice_record.update_column(:webhook_uuid, uuid)
|
|
66
|
+
rescue => e
|
|
67
|
+
invoice_record.update_column(:status, :creation_error)
|
|
68
|
+
raise e
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.udate_invoice_attributes(invoice_id, json_body)
|
|
73
|
+
invoice_record = ACube::InvoiceRecord.find_by(webhook_uuid: invoice_id)
|
|
74
|
+
invoice_record.update_column(:json_body, json_body)
|
|
75
|
+
|
|
76
|
+
begin
|
|
77
|
+
downloaded_pdf = ACube::Endpoint::Invoices.new.download(invoice_record.webhook_uuid)
|
|
78
|
+
downloaded_pdf.rewind
|
|
79
|
+
invoice_record.pdf.attach(io: downloaded_pdf, filename: "#{invoice_record.webhook_uuid}-invoice.pdf", content_type: 'application/pdf')
|
|
80
|
+
invoice_record.update_column(:status, :downloaded)
|
|
81
|
+
rescue => e
|
|
82
|
+
invoice_record.update_column(:status, :download_error)
|
|
83
|
+
raise e
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def self.update_invoice_status(webhook_body)
|
|
88
|
+
notification = JSON.parse(webhook_body, symbolize_names: true)
|
|
89
|
+
invoice_record = ACube::InvoiceRecord.find_by(webhook_uuid: notification[:notification][:invoice_uuid])
|
|
90
|
+
|
|
91
|
+
status = case notification[:notification][:type]
|
|
92
|
+
when "MC" then :not_received
|
|
93
|
+
when "AT" then :not_received
|
|
94
|
+
when "RC" then :delivered
|
|
95
|
+
when "NS" then :rejected
|
|
96
|
+
else :notification_error
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
invoice_record.update_column(:status, status)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
def initialize(supplier, customer, invoice, format)
|
|
104
|
+
@format = format
|
|
105
|
+
|
|
106
|
+
@supplier = ACube::Schema::Header::Supplier.from(supplier)
|
|
107
|
+
@customer = ACube::Schema::Header::Customer.from(customer)
|
|
108
|
+
@invoice = ACube::Schema::Body.from(invoice)
|
|
109
|
+
|
|
110
|
+
@invoice_base_record = invoice
|
|
111
|
+
|
|
112
|
+
@header = ACube::Schema::Header::Header.new(@supplier, @customer)
|
|
113
|
+
@document = ACube::Schema::Document.new(@header, @invoice)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
module Schema
|
|
3
|
+
class Body
|
|
4
|
+
DOCUMENT_KINDS = %w[TD01 TD02 TD03 TD04 TD05 TD06 TD16 TD17 TD18 TD19 TD20 TD21 TD22 TD23 TD24 TD25 TD26 TD27 TD28].freeze
|
|
5
|
+
PAYMENT_METHODS = %w[MP01 MP02 MP03 MP04 MP05 MP06 MP07 MP08 MP09 MP10 MP11 MP12 MP13 MP14 MP15 MP16 MP17 MP18 MP19 MP20 MP21 MP22 MP23].freeze
|
|
6
|
+
|
|
7
|
+
attr_accessor :document_kind, :date
|
|
8
|
+
attr_accessor :total_price
|
|
9
|
+
attr_accessor :connected_progressive
|
|
10
|
+
attr_accessor :description
|
|
11
|
+
attr_accessor :quantity
|
|
12
|
+
attr_accessor :causal
|
|
13
|
+
attr_accessor :payment_max_date
|
|
14
|
+
attr_accessor :payment_terms, :payment_method
|
|
15
|
+
attr_reader :progressive
|
|
16
|
+
|
|
17
|
+
def self.from(invoice)
|
|
18
|
+
new.tap do |body|
|
|
19
|
+
invoice.transaction_data.each do |key, value|
|
|
20
|
+
value = value.is_a?(Symbol) ? invoice.send(value) : value
|
|
21
|
+
body.send("#{key}=", value)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def set_progressive(progressive)
|
|
27
|
+
@progressive = progressive
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_xml
|
|
31
|
+
Nokogiri::XML::Builder.new do |xml|
|
|
32
|
+
xml.FatturaElettronicaBody {
|
|
33
|
+
xml.DatiGenerali {
|
|
34
|
+
xml.DatiGeneraliDocumento {
|
|
35
|
+
xml.TipoDocumento document_kind
|
|
36
|
+
xml.Divisa "EUR"
|
|
37
|
+
xml.Data date.strftime("%Y-%m-%d")
|
|
38
|
+
xml.Numero progressive
|
|
39
|
+
xml.ImportoTotaleDocumento total_price
|
|
40
|
+
xml.Causale causal if causal
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (document_kind == :TD04)
|
|
44
|
+
xml.DatiFattureCollegate {
|
|
45
|
+
xml.IdDocumento connected_progressive
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
xml.DatiBeniServizi {
|
|
51
|
+
xml.DettaglioLinee {
|
|
52
|
+
xml.NumeroLinea 1
|
|
53
|
+
xml.Descrizione description
|
|
54
|
+
xml.Quantita ("%f" % quantity.to_f)
|
|
55
|
+
xml.PrezzoUnitario ("%f" % unitary_price.to_f)
|
|
56
|
+
xml.PrezzoTotale ("%f" % price_no_vat.to_f)
|
|
57
|
+
xml.AliquotaIVA ("%.2f" % (ACube.vat_amount * 100).to_f)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
xml.DatiRiepilogo {
|
|
61
|
+
xml.AliquotaIVA ("%.2f" % (ACube.vat_amount * 100).to_f)
|
|
62
|
+
xml.ImponibileImporto ("%f" % price_no_vat.to_f)
|
|
63
|
+
xml.Imposta ("%f" % vat_amount.to_f)
|
|
64
|
+
xml.EsigibilitaIVA "I"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
xml.DatiPagamento {
|
|
69
|
+
xml.CondizioniPagamento payment_terms
|
|
70
|
+
xml.DettaglioPagamento {
|
|
71
|
+
xml.ModalitaPagamento payment_method
|
|
72
|
+
xml.DataScadenzaPagamento payment_max_date.strftime("%Y-%m-%d")
|
|
73
|
+
xml.ImportoPagamento ("%f" % total_price.to_f)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
end.to_xml(save_with: 2)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
def unitary_price
|
|
82
|
+
unitary = total_price / quantity
|
|
83
|
+
unitary_vat = unitary * ACube.vat_amount
|
|
84
|
+
unitary - unitary_vat
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def price_no_vat
|
|
88
|
+
total_price - vat_amount
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def vat_amount
|
|
92
|
+
total_price * ACube.vat_amount
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
module Schema
|
|
3
|
+
class Document
|
|
4
|
+
TRANSMISSION_FORMATS = %w[FPR12 FPA12].freeze
|
|
5
|
+
|
|
6
|
+
attr_accessor :body, :header
|
|
7
|
+
attr_accessor :progressive, :transmission_format
|
|
8
|
+
|
|
9
|
+
def initialize(header, body)
|
|
10
|
+
@header = header
|
|
11
|
+
@body = body
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def fill_with(transmission_format:, progressive:)
|
|
15
|
+
@transmission_format = transmission_format
|
|
16
|
+
@progressive = progressive
|
|
17
|
+
|
|
18
|
+
header.transmission_format = transmission_format
|
|
19
|
+
header.progressive = progressive
|
|
20
|
+
body.set_progressive(progressive)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_xml
|
|
24
|
+
Nokogiri::XML::Builder.new(encoding: 'UTF-8', namespace_inheritance:false) do |xml|
|
|
25
|
+
xml["p"].FatturaElettronica(
|
|
26
|
+
"versione" => header.transmission_format,
|
|
27
|
+
"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#",
|
|
28
|
+
"xmlns:p" => "http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2",
|
|
29
|
+
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
|
30
|
+
"xsi:schemaLocation" => "http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2 http://www.fatturapa.gov.it/export/fatturazione/sdi/fatturapa/v1.2/Schema_del_file_xml_FatturaPA_versione_1.2.xsd"
|
|
31
|
+
) do
|
|
32
|
+
xml << header.to_xml
|
|
33
|
+
xml << body.to_xml
|
|
34
|
+
end
|
|
35
|
+
end.to_xml
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
module Schema
|
|
3
|
+
module Header
|
|
4
|
+
class Customer
|
|
5
|
+
attr_accessor :vat_fiscal_id
|
|
6
|
+
attr_accessor :fiscal_code
|
|
7
|
+
attr_accessor :first_name, :last_name, :denomination, :title, :eori_code
|
|
8
|
+
attr_accessor :address, :civic_number, :zip, :city, :province, :nation
|
|
9
|
+
|
|
10
|
+
def self.from(customer)
|
|
11
|
+
new.tap do |cust|
|
|
12
|
+
customer.customer_data.each do |key, value|
|
|
13
|
+
value = value.is_a?(Symbol) ? customer.send(value).to_s : value.to_s
|
|
14
|
+
cust.send("#{key}=", value)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_xml
|
|
20
|
+
Nokogiri::XML::Builder.new do |xml|
|
|
21
|
+
xml.CessionarioCommittente {
|
|
22
|
+
xml.DatiAnagrafici {
|
|
23
|
+
xml.CodiceFiscale fiscal_code
|
|
24
|
+
xml.Anagrafica {
|
|
25
|
+
if (first_name && last_name)
|
|
26
|
+
xml.Nome first_name
|
|
27
|
+
xml.Cognome last_name
|
|
28
|
+
else
|
|
29
|
+
xml.Denominazione denomination
|
|
30
|
+
end
|
|
31
|
+
xml.Titolo title if title
|
|
32
|
+
xml.CodEORI eori_code if eori_code
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
xml.Sede {
|
|
36
|
+
xml.Indirizzo address
|
|
37
|
+
xml.NumeroCivico civic_number if civic_number
|
|
38
|
+
xml.CAP zip
|
|
39
|
+
xml.Comune city
|
|
40
|
+
xml.Provincia province
|
|
41
|
+
xml.Nazione nation
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
end.to_xml(save_with: 2)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
module Schema
|
|
3
|
+
module Header
|
|
4
|
+
class Header
|
|
5
|
+
attr_accessor :supplier, :customer
|
|
6
|
+
attr_accessor :transmission_format
|
|
7
|
+
attr_accessor :progressive
|
|
8
|
+
|
|
9
|
+
def initialize(supplier, customer)
|
|
10
|
+
@supplier = supplier
|
|
11
|
+
@customer = customer
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_xml
|
|
15
|
+
Nokogiri::XML::Builder.new do |xml|
|
|
16
|
+
xml.FatturaElettronicaHeader {
|
|
17
|
+
xml.DatiTrasmissione {
|
|
18
|
+
xml.IdTrasmittente {
|
|
19
|
+
xml.IdPaese ACube.transmission_nation_id
|
|
20
|
+
xml.IdCodice ACube.transmission_id_code
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
xml.ProgressivoInvio progressive
|
|
24
|
+
xml.FormatoTrasmissione transmission_format
|
|
25
|
+
xml.CodiceDestinatario "0000000"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
xml << supplier.to_xml
|
|
29
|
+
xml << customer.to_xml
|
|
30
|
+
}
|
|
31
|
+
end.to_xml(save_with: 2)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
module Schema
|
|
3
|
+
module Header
|
|
4
|
+
class Supplier
|
|
5
|
+
FISCAL_REGIMES = %w[RF01 RF02 RF03 RF04 RF05 RF06 RF07 RF08 RF09 RF10 RF11 RF12 RF13 RF14 RF15 RF16 RF17 RF18 RF19].freeze
|
|
6
|
+
|
|
7
|
+
attr_accessor :id_nation, :id_tax_code
|
|
8
|
+
attr_accessor :fiscal_code
|
|
9
|
+
attr_accessor :first_name, :last_name, :denomination, :title, :eori_code
|
|
10
|
+
attr_accessor :albo_professional, :aldo_province, :albo_subscription, :albo_subscription_date
|
|
11
|
+
attr_accessor :fiscal_regime
|
|
12
|
+
attr_accessor :address, :civic_number, :zip, :city, :province, :nation
|
|
13
|
+
|
|
14
|
+
def self.from(supplier)
|
|
15
|
+
new.tap do |supp|
|
|
16
|
+
supplier.supplier_data.each do |key, value|
|
|
17
|
+
value = value.is_a?(Symbol) ? supplier.send(value).to_s : value.to_s
|
|
18
|
+
supp.send("#{key}=", value)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_xml
|
|
24
|
+
Nokogiri::XML::Builder.new do |xml|
|
|
25
|
+
xml.CedentePrestatore {
|
|
26
|
+
xml.DatiAnagrafici {
|
|
27
|
+
xml.IdFiscaleIVA {
|
|
28
|
+
xml.IdPaese id_nation
|
|
29
|
+
xml.IdCodice id_tax_code
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
xml.Anagrafica {
|
|
33
|
+
if (first_name && last_name)
|
|
34
|
+
xml.Nome first_name
|
|
35
|
+
xml.Cognome last_name
|
|
36
|
+
else
|
|
37
|
+
xml.Denominazione denomination
|
|
38
|
+
end
|
|
39
|
+
xml.Titolo title if title
|
|
40
|
+
xml.CodEORI eori_code if eori_code
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
xml.RegimeFiscale fiscal_regime
|
|
44
|
+
xml.CodiceFiscale fiscal_code if fiscal_code
|
|
45
|
+
|
|
46
|
+
xml.AlboProfessionale albo_professional if albo_professional
|
|
47
|
+
xml.ProvinciaAlbo aldo_province if aldo_province
|
|
48
|
+
xml.NumeroIscrizioneAlbo albo_subscription if albo_subscription
|
|
49
|
+
xml.DataIscrizioneAlbo albo_subscription_date if albo_subscription_date
|
|
50
|
+
}
|
|
51
|
+
xml.Sede {
|
|
52
|
+
xml.Indirizzo address
|
|
53
|
+
xml.NumeroCivico civic_number if civic_number
|
|
54
|
+
xml.CAP zip
|
|
55
|
+
xml.Comune city
|
|
56
|
+
xml.Provincia province
|
|
57
|
+
xml.Nazione nation
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
end.to_xml(save_with: 2)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
class SignatureChecker
|
|
3
|
+
class InvalidSignatureError < StandardError; end
|
|
4
|
+
|
|
5
|
+
HASH_ALGORIGHTM = "sha256"
|
|
6
|
+
DEFAULT_KEY = "acube"
|
|
7
|
+
DEFAULT_GPG = <<-GPG
|
|
8
|
+
-----BEGIN PUBLIC KEY-----
|
|
9
|
+
MCowBQYDK2VwAyEAvZlhiFh4aORWSC9hKZvZyKYgn2g2VeSguWoxu4fbqRI=
|
|
10
|
+
-----END PUBLIC KEY-----
|
|
11
|
+
GPG
|
|
12
|
+
|
|
13
|
+
def self.verify_signature(request, payload)
|
|
14
|
+
new(request.headers, payload).verify_signature
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_reader :headers, :payload
|
|
18
|
+
attr_accessor :signature, :input, :digest
|
|
19
|
+
def initialize(headers, payload)
|
|
20
|
+
@headers = headers
|
|
21
|
+
if (!ACube.webhook_secret_key.nil? && !ACube.webhook_secret.nil?)
|
|
22
|
+
raise InvalidSignatureError unless headers.has_key?("Authorization") && headers["Authorization"] == "#{ACube.webhook_secret_key} #{ACube.webhook_secret}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
raise InvalidSignatureError unless headers.has_key?("signature") && headers.has_key?("signature-input") && headers.has_key?("signature-digest")
|
|
26
|
+
signature, input, digest = headers["signature"], headers["signature-input"], headers["signature-digest"]
|
|
27
|
+
|
|
28
|
+
@payload = payload
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def verify_signature
|
|
32
|
+
raw_data = url + params.sort.join
|
|
33
|
+
OpenSSL::HMAC.digest(HASH_ALGORITHM, signature_gpg, raw_data)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
def signature_key
|
|
38
|
+
ACube.webhook_signature_key || DEFAULT_KEY
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def signature_gpg
|
|
42
|
+
ACube.webhook_signature_gpg || DEFAULT_GPG
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
module Support
|
|
3
|
+
module Customer
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
cattr_reader :customer_data
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
protected
|
|
9
|
+
def self.as_customer(&block)
|
|
10
|
+
config = CustomerBuilder.new
|
|
11
|
+
yield(config)
|
|
12
|
+
@@customer_data = config.finalize.dup
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class CustomerBuilder
|
|
17
|
+
@@attributes = ACube::Schema::Header::Customer.instance_methods.select {|m| m.ends_with?("=") && m.starts_with?(/\w/) }
|
|
18
|
+
@customer_data = {}
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@customer_data = {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def finalize
|
|
25
|
+
@customer_data
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def method_missing(method, value)
|
|
29
|
+
if (@@attributes.include?(method))
|
|
30
|
+
@customer_data[method[0..-2]] = value
|
|
31
|
+
else
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_customer
|
|
38
|
+
ACube::Schema::Header::Customer.from(self)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
module Support
|
|
3
|
+
module Supplier
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
cattr_reader :supplier_data
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
protected
|
|
9
|
+
def self.as_supplier(&block)
|
|
10
|
+
config = SupplierBuilder.new
|
|
11
|
+
yield(config)
|
|
12
|
+
@@supplier_data = config.finalize.dup
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class SupplierBuilder
|
|
17
|
+
@@attributes = ACube::Schema::Header::Supplier.instance_methods.select {|m| m.ends_with?("=") && m.starts_with?(/\w/) }
|
|
18
|
+
@supplier_data = {}
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@supplier_data = {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def finalize
|
|
25
|
+
@supplier_data
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def method_missing(method, value)
|
|
29
|
+
if (@@attributes.include?(method))
|
|
30
|
+
@supplier_data[method[0..-2]] = value
|
|
31
|
+
else
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_supplier
|
|
38
|
+
ACube::Schema::Header::Supplier.from(self)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module ACube
|
|
2
|
+
module Support
|
|
3
|
+
module Transaction
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
cattr_reader :transaction_data
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
protected
|
|
9
|
+
def self.as_transaction(&block)
|
|
10
|
+
config = TransactionBuilder.new
|
|
11
|
+
yield(config)
|
|
12
|
+
@@transaction_data = config.finalize.dup
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class TransactionBuilder
|
|
17
|
+
@@attributes = ACube::Schema::Body.instance_methods.select {|m| m.ends_with?("=") && m.starts_with?(/\w/) }
|
|
18
|
+
@transaction_data = {}
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@transaction_data = {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def finalize
|
|
25
|
+
@transaction_data
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def method_missing(method, value)
|
|
29
|
+
if (@@attributes.include?(method))
|
|
30
|
+
@transaction_data[method[0..-2]] = value
|
|
31
|
+
else
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_transaction
|
|
38
|
+
ACube::Schema::Body.from(self)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/acube.rb
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require "active_support"
|
|
2
|
+
require "active_support/rails"
|
|
3
|
+
|
|
4
|
+
require "acube/version"
|
|
5
|
+
require "acube/engine"
|
|
6
|
+
|
|
7
|
+
require "zeitwerk"
|
|
8
|
+
|
|
9
|
+
module ACube
|
|
10
|
+
extend ActiveSupport::Autoload
|
|
11
|
+
|
|
12
|
+
mattr_accessor :invoice_endpoint
|
|
13
|
+
mattr_accessor :common_endpoint
|
|
14
|
+
|
|
15
|
+
mattr_accessor :username
|
|
16
|
+
mattr_accessor :password
|
|
17
|
+
|
|
18
|
+
mattr_accessor :invoice_base_class, default: "ApplicationRecord"
|
|
19
|
+
|
|
20
|
+
mattr_accessor :webhook_base_class, default: "ApplicationController"
|
|
21
|
+
mattr_accessor :webhook_endpoint, default: "/acube-api/webhook"
|
|
22
|
+
mattr_accessor :webhook_secret_key
|
|
23
|
+
mattr_accessor :webhook_secret
|
|
24
|
+
|
|
25
|
+
mattr_accessor :webhook_signature_key
|
|
26
|
+
mattr_accessor :webhook_signature_gpg
|
|
27
|
+
|
|
28
|
+
mattr_accessor :auth_token_cache_key, default: "__acube__auth__token"
|
|
29
|
+
|
|
30
|
+
mattr_accessor :invoice_print_theme, default: "standard"
|
|
31
|
+
mattr_accessor :progressive_string, default: -> (number) { "#{number}" }
|
|
32
|
+
mattr_accessor :vat_amount, default: 0.22
|
|
33
|
+
|
|
34
|
+
mattr_accessor :transmission_nation_id, default: "IT"
|
|
35
|
+
mattr_accessor :transmission_id_code, default: "10442360961"
|
|
36
|
+
|
|
37
|
+
def self.configure
|
|
38
|
+
yield(self)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
autoload :Attribute
|
|
42
|
+
autoload :SignatureChecker
|
|
43
|
+
autoload :Invoicer
|
|
44
|
+
|
|
45
|
+
module Endpoint
|
|
46
|
+
extend ActiveSupport::Autoload
|
|
47
|
+
|
|
48
|
+
autoload :CommonBase
|
|
49
|
+
autoload :ItApiBase
|
|
50
|
+
|
|
51
|
+
autoload :Auth
|
|
52
|
+
autoload :Invoices
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module Schema
|
|
56
|
+
extend ActiveSupport::Autoload
|
|
57
|
+
|
|
58
|
+
module Header
|
|
59
|
+
extend ActiveSupport::Autoload
|
|
60
|
+
|
|
61
|
+
autoload :Supplier
|
|
62
|
+
autoload :Customer
|
|
63
|
+
autoload :Header
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
autoload :Body
|
|
67
|
+
autoload :Document
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
module Support
|
|
71
|
+
extend ActiveSupport::Autoload
|
|
72
|
+
|
|
73
|
+
autoload :Customer
|
|
74
|
+
autoload :Supplier
|
|
75
|
+
autoload :Transaction
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
loader = Zeitwerk::Loader.for_gem
|
|
80
|
+
loader.ignore("#{__dir__}/generators")
|
|
81
|
+
loader.inflector.inflect(
|
|
82
|
+
"acube" => "ACube"
|
|
83
|
+
)
|
|
84
|
+
loader.setup
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
|
|
5
|
+
module ACube
|
|
6
|
+
module Generators
|
|
7
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path('templates', __dir__)
|
|
9
|
+
|
|
10
|
+
def create_migrations
|
|
11
|
+
rails_command "railties:install:migrations FROM=active_storage,acube", inline: true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: acube-rails
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.4
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Pietro Moro
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2023-07-03 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 7.0.0
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 7.0.0
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: faraday
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '2.7'
|
|
34
|
+
- - ">="
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: 2.7.6
|
|
37
|
+
type: :runtime
|
|
38
|
+
prerelease: false
|
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
40
|
+
requirements:
|
|
41
|
+
- - "~>"
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '2.7'
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 2.7.6
|
|
47
|
+
description: ACube api wrapper for rails
|
|
48
|
+
email:
|
|
49
|
+
- pietro@pietromoro.dev
|
|
50
|
+
executables: []
|
|
51
|
+
extensions: []
|
|
52
|
+
extra_rdoc_files: []
|
|
53
|
+
files:
|
|
54
|
+
- MIT-LICENSE
|
|
55
|
+
- README.md
|
|
56
|
+
- Rakefile
|
|
57
|
+
- app/models/acube/invoice_record.rb
|
|
58
|
+
- app/models/acube/record.rb
|
|
59
|
+
- config/application.rb
|
|
60
|
+
- config/initializers/zeitwerk.rb
|
|
61
|
+
- config/routes.rb
|
|
62
|
+
- db/migrate/20230624195146_create_acube_invoice_records.rb
|
|
63
|
+
- lib/acube.rb
|
|
64
|
+
- lib/acube/attribute.rb
|
|
65
|
+
- lib/acube/endpoint/auth.rb
|
|
66
|
+
- lib/acube/endpoint/common_base.rb
|
|
67
|
+
- lib/acube/endpoint/invoices.rb
|
|
68
|
+
- lib/acube/endpoint/it_api_base.rb
|
|
69
|
+
- lib/acube/engine.rb
|
|
70
|
+
- lib/acube/invoicer.rb
|
|
71
|
+
- lib/acube/schema/body.rb
|
|
72
|
+
- lib/acube/schema/document.rb
|
|
73
|
+
- lib/acube/schema/header/customer.rb
|
|
74
|
+
- lib/acube/schema/header/header.rb
|
|
75
|
+
- lib/acube/schema/header/supplier.rb
|
|
76
|
+
- lib/acube/signature_checker.rb
|
|
77
|
+
- lib/acube/support/customer.rb
|
|
78
|
+
- lib/acube/support/supplier.rb
|
|
79
|
+
- lib/acube/support/transaction.rb
|
|
80
|
+
- lib/acube/version.rb
|
|
81
|
+
- lib/generators/a_cube/install/install_generator.rb
|
|
82
|
+
- lib/tasks/acube.rake
|
|
83
|
+
homepage: https://github.com/pietromoro/acube-rails
|
|
84
|
+
licenses:
|
|
85
|
+
- MIT
|
|
86
|
+
metadata:
|
|
87
|
+
homepage_uri: https://github.com/pietromoro/acube-rails
|
|
88
|
+
source_code_uri: https://github.com/pietromoro/acube-rails
|
|
89
|
+
changelog_uri: https://github.com/pietromoro/acube-rails/blob/master/CHANGELOG.md
|
|
90
|
+
post_install_message:
|
|
91
|
+
rdoc_options: []
|
|
92
|
+
require_paths:
|
|
93
|
+
- lib
|
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: '0'
|
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
requirements: []
|
|
105
|
+
rubygems_version: 3.3.7
|
|
106
|
+
signing_key:
|
|
107
|
+
specification_version: 4
|
|
108
|
+
summary: ACube api wrapper for rails
|
|
109
|
+
test_files: []
|