acube-rails 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|