huginn_acumen_order_agent 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e43425e52ee96e8537e96c346347c2594e0e0da21482cd4630fc1d8a916a355a
4
+ data.tar.gz: 6ff32c412a21ac888e7a909d4356621fb0ef17fe068e690e7e0f066ee4245650
5
+ SHA512:
6
+ metadata.gz: 7cced5dbc343bf4d889a331025551a6d0d602e4b8cee4e22578a74467bb20b4bb16b3373789eec7dafb3bb34793014522af71ab322ffcddee9c9b2f4e73784a3
7
+ data.tar.gz: 31dcd11db687b62b6816bc06806d844696a5d2351794854b79c12ce71393dd9e6543e26d7f414437d3e98bf8ebd163fd42f936ebf3b55892f345cd77f7ac3fd5
data/LICENSE.txt ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2020 Jacob Spizziri
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,10 @@
1
+ require 'huginn_agent'
2
+
3
+ HuginnAgent.load 'huginn_acumen_order_agent/concerns/acumen_query_concern'
4
+ HuginnAgent.load 'huginn_acumen_order_agent/concerns/invoice_query_concern'
5
+ HuginnAgent.load 'huginn_acumen_order_agent/concerns/invoice_detail_query_concern'
6
+
7
+ HuginnAgent.load 'huginn_acumen_order_agent/acumen_client'
8
+ HuginnAgent.load 'huginn_acumen_order_agent/acumen_order_error'
9
+
10
+ HuginnAgent.register 'huginn_acumen_order_agent/acumen_order_agent'
@@ -0,0 +1,116 @@
1
+ class AcumenOrderClient
2
+ @faraday
3
+ @auth
4
+
5
+ def initialize(faraday, auth)
6
+ @faraday = faraday
7
+ @auth = auth
8
+ end
9
+
10
+ def get_invoices(order_codes)
11
+ body = build_invoice_request(order_codes)
12
+ response = execute_in_list_query(body, {})
13
+ get_results(response, 'Invoice')
14
+ end
15
+
16
+ def get_invoice_details(invoice_ids)
17
+ body = build_invoice_detail_query(invoice_ids)
18
+ response = execute_in_list_query(body, {})
19
+ get_results(response, 'Invoice_Detail')
20
+ end
21
+
22
+ def execute_query(body, headers)
23
+ response = @faraday.run_request(:post, "#{@auth['endpoint']}Query", body, headers)
24
+ ::MultiXml.parse(response.body, {})
25
+ end
26
+
27
+ def execute_in_list_query(body, headers)
28
+ response = @faraday.run_request(:post, "#{@auth['endpoint']}QueryByInList", body, headers)
29
+ ::MultiXml.parse(response.body, {})
30
+ end
31
+
32
+ def get_results(response, name)
33
+ result_set = response['Envelope']['Body']['acusoapResponse']['result_set.' + name]
34
+ results = result_set.nil? ? [] : result_set[name]
35
+ results.is_a?(Array) ? results : [results]
36
+ end
37
+
38
+ private
39
+
40
+ def build_invoice_request(codes)
41
+ <<~XML
42
+ <acusoapRequest>
43
+ #{build_acumen_query_auth()}
44
+ <query>
45
+ <statement>
46
+ <column_name>Invoice.Order_Code</column_name>
47
+ <comparator>in</comparator>
48
+ <value>#{codes.join(',')}</value>
49
+ </statement>
50
+ </query>
51
+ <requested_output>
52
+ <view_owner_table_name>Invoice</view_owner_table_name>
53
+ <view_name>InvoiceAllRead</view_name>
54
+ <column_name>Invoice.Invoice_ID</column_name>
55
+ <column_name>Invoice.Modified_Date</column_name>
56
+ <column_name>Invoice.Order_Code</column_name>
57
+ <column_name>Invoice.Order_Date</column_name>
58
+ <column_name>Invoice.Status</column_name>
59
+ <column_name>Invoice.Customer_Name</column_name>
60
+ <column_name>Invoice.Tracking_Num</column_name>
61
+ </requested_output>
62
+ </acusoapRequest>
63
+ XML
64
+ end
65
+
66
+ def build_invoice_detail_query(invoice_ids)
67
+ <<~XML
68
+ <acusoapRequest>
69
+ #{build_acumen_query_auth()}
70
+ <query>
71
+ <statement>
72
+ <column_name>Invoice_Detail.Invoice_ID</column_name>
73
+ <comparator>in</comparator>
74
+ <value>#{invoice_ids.join(',')}</value>
75
+ </statement>
76
+ </query>
77
+ <requested_output>
78
+ <view_owner_table_name>Invoice_Detail</view_owner_table_name>
79
+ <view_name>Invoice_DetailAllRead</view_name>
80
+ <column_name>Invoice_Detail.Invoice_DETAIL_ID</column_name>
81
+ <column_name>Invoice_Detail.Title</column_name>
82
+ <column_name>Invoice_Detail.ProdCode</column_name>
83
+ <column_name>Invoice_Detail.Ordered</column_name>
84
+ <column_name>Invoice_Detail.Ship</column_name>
85
+ <column_name>Invoice_Detail.BO</column_name>
86
+ <column_name>Invoice_Detail.List</column_name>
87
+ <column_name>Invoice_Detail.Back_Order_ID</column_name>
88
+ <column_name>Invoice_Detail.Mktg_Code</column_name>
89
+ <column_name>Invoice_Detail.Order_Number</column_name>
90
+ <column_name>Invoice_Detail.BO_Reason</column_name>
91
+ <column_name>Invoice_Detail.Quant_Ship_Confirm</column_name>
92
+ <column_name>Invoice_Detail.Quant_BO_Confirm</column_name>
93
+ <column_name>Invoice_Detail.BO_Prebill</column_name>
94
+ <column_name>Invoice_Detail.Back_Order_Special_ID</column_name>
95
+ <column_name>Invoice_Detail.PO_Number</column_name>
96
+ <column_name>Invoice_Detail.RF_ShipConfirm_Comment</column_name>
97
+ <column_name>Invoice_Detail.Old_BO</column_name>
98
+ <column_name>Invoice_Detail.Invoice_ID</column_name>
99
+ <column_name>Invoice_Detail.Modified_Date</column_name>
100
+ <column_name>Invoice_Detail.BO_Original_Invoice_DETAIL_ID</column_name>
101
+ </requested_output>
102
+ </acusoapRequest>
103
+ XML
104
+ end
105
+
106
+ def build_acumen_query_auth()
107
+ <<~XML
108
+ <authentication>
109
+ <site_code>#{@auth['site_code']}</site_code>
110
+ <password>#{@auth['password']}</password>
111
+ </authentication>
112
+ <message_version>1.00</message_version>
113
+ XML
114
+ end
115
+
116
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agents
4
+ class AcumenOrderAgent < Agent
5
+ include WebRequestConcern
6
+ include AcumenOrderQueryConcern
7
+ include InvoiceQueryConcern
8
+ include InvoiceDetailQueryConcern
9
+
10
+ default_schedule '12h'
11
+
12
+ can_dry_run!
13
+ default_schedule 'never'
14
+
15
+ description <<-MD
16
+ Huginn agent for retrieving sane ACUMEN invoice data.
17
+
18
+ ## Agent Options
19
+ The following outlines the available options in this agent
20
+
21
+ ### Acumen Connection
22
+ * endpoint: The root URL for the Acumen API
23
+ * site_code: The site code from Acumen
24
+ * password: The Acumen API password
25
+ * output_mode - not required ('clean' or 'merge', defaults to 'clean')
26
+
27
+ ### Payload Status
28
+
29
+ `status: 200`: Indicates a true success. The agent has output the full
30
+ range of expected data.
31
+
32
+ `status: 206`: Indicates a partial success. The products within the bundle
33
+ are vaild, but the bundle _may_ be missing products that were somehow invalid.
34
+
35
+ `status: 500`: Indicates a processing error. This may represent a complete
36
+ process failure, but may also be issued in parallel to a `202` payload.
37
+
38
+ Because this agent receives an array of Order Codes as input, errors will be issued in
39
+ such a way that product processing can recover when possible. Errors that occur within
40
+ a specific product bundle will emit an error event, but the agent will then move
41
+ forward processing the next bundle.
42
+
43
+ For example, if this agent receives two products as input (`A` and `B`), and we fail to
44
+ load the Inv_Product record for product `A`, the agent would emit an error payload of:
45
+
46
+ ```
47
+ {
48
+ status: 500,
49
+ scope: 'Fetch Inv_Product Data',
50
+ message: 'Failed to lookup Inv_Product record for Product A',
51
+ data: { product_id: 123 },
52
+ trace: [ ... ]
53
+ }
54
+ ```
55
+
56
+ The goal of this approach is to ensure the agent outputs as much data as reasonably possible
57
+ with each execution. If there is an error in the Paperback version of a title, that shouldn't
58
+ prevent this agent from returning the Hardcover version.
59
+
60
+ MD
61
+
62
+ def default_options
63
+ {
64
+ 'endpoint' => 'https://example.com',
65
+ 'site_code' => '',
66
+ 'password' => '',
67
+ 'output_mode' => 'clean',
68
+ }
69
+ end
70
+
71
+ def validate_options
72
+ unless options['endpoint'].present?
73
+ errors.add(:base, 'endpoint is a required field')
74
+ end
75
+
76
+ unless options['site_code'].present?
77
+ errors.add(:base, 'site_code is a required field')
78
+ end
79
+
80
+ unless options['password'].present?
81
+ errors.add(:base, 'password is a required field')
82
+ end
83
+
84
+ if options['output_mode'].present? && !options['output_mode'].to_s.include?('{') && !%[clean merge].include?(options['output_mode'].to_s)
85
+ errors.add(:base, "if provided, output_mode must be 'clean' or 'merge'")
86
+ end
87
+ end
88
+
89
+ def working?
90
+ received_event_without_error?
91
+ end
92
+
93
+ def check
94
+ handle interpolated['payload'].presence || {}
95
+ end
96
+
97
+ def receive(incoming_events)
98
+ incoming_events.each do |event|
99
+ handle(event)
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def handle(event)
106
+ # Process agent options
107
+ endpoint = interpolated['endpoint']
108
+ endpoint = endpoint += '/' unless endpoint.end_with?('/')
109
+ site_code = interpolated['site_code']
110
+ password = interpolated['password']
111
+
112
+ # Configure the Acumen Client
113
+ auth = {
114
+ 'site_code' => site_code,
115
+ 'password' => password,
116
+ 'endpoint' => endpoint,
117
+ }
118
+ client = AcumenOrderClient.new(faraday, auth)
119
+ data = event.payload
120
+ order_codes = event.payload['order_codes']
121
+ new_event = interpolated['output_mode'].to_s == 'merge' ? data.dup : {}
122
+
123
+ begin
124
+ invoices = fetch_invoice_data(client, order_codes)
125
+
126
+ unless invoices.blank?
127
+ invoices = fetch_invoice_details(client, invoices)
128
+ create_event payload: new_event.merge(
129
+ invoices: invoices,
130
+ status: 200
131
+ )
132
+ end
133
+ rescue AcumenOrderError => e
134
+ issue_error(e, new_event)
135
+ end
136
+ end
137
+
138
+ def issue_error(error, new_event, status = 500)
139
+ # NOTE: Status is intentionally included on the top-level payload so that other
140
+ # agents can look for a `payload[:status]` of either 200 or 500 to distinguish
141
+ # between success and failure states
142
+ create_event payload: new_event.merge(
143
+ status: status,
144
+ scope: error.scope,
145
+ message: error.message,
146
+ original_error: error.original_error,
147
+ data: error.data,
148
+ trace: error.original_error.backtrace,
149
+ )
150
+ end
151
+
152
+ end
153
+ end
@@ -0,0 +1,10 @@
1
+ class AcumenOrderError < StandardError
2
+ attr_reader :scope, :data, :original_error
3
+
4
+ def initialize(scope, message, data, original_error)
5
+ @scope = scope
6
+ @data = data
7
+ @original_error = original_error
8
+ super(message)
9
+ end
10
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module contains the baseline utility methods used in the more specific
4
+ # data concerns
5
+ module AcumenOrderQueryConcern
6
+ extend ActiveSupport::Concern
7
+
8
+ protected
9
+
10
+ # Maps Acumen XML data to a hash object as specified in the provided `field_map`
11
+ # The field map is a hash of { source_field: target_field }
12
+ def response_mapper(data, field_map)
13
+ result = {}
14
+
15
+ field_map.each do |source_field, target_field|
16
+ result[target_field] = get_field_value(data, source_field)
17
+ end
18
+
19
+ return result
20
+ end
21
+
22
+ # Utility function to retrieve a value from an XML field
23
+ def get_field_value(data, field_name)
24
+ data[field_name]['__content__'] if data[field_name]
25
+ end
26
+
27
+ # Returns a quantitative field value (e.g. weight) as a Schema.org/QuantitativeValue
28
+ # object
29
+ def get_quantitative_value(value, unit)
30
+ {
31
+ '@type' => 'QuantitativeValue',
32
+ 'value' => value,
33
+ 'unitText' => unit,
34
+ 'unitCode' => (UNIT_MAP[unit] if unit),
35
+ } if value
36
+ end
37
+
38
+ # Emits an error payload event to facilitate better debugging/logging
39
+ # NOTE: The `error` here is expected to be an instance of AcumenOrderError
40
+ def issue_error(error, status = 500)
41
+ # NOTE: Status is intentionally included on the top-level payload so that other
42
+ # agents can look for a `payload[:status]` of either 200 or 500 to distinguish
43
+ # between success and failure states
44
+ create_event payload: {
45
+ status: status,
46
+ scope: error.scope,
47
+ message: error.message,
48
+ original_error: error.original_error,
49
+ data: error.data,
50
+ trace: error.original_error.backtrace,
51
+ }
52
+ end
53
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module is responsible for reading/processing the Invoice_Detail table. This table
4
+ # contains information related to individual line items within an invoice.
5
+ module InvoiceDetailQueryConcern
6
+ extend AcumenOrderQueryConcern
7
+
8
+ # Update the provided products with their associated marketing data
9
+ # NOTE: The `products` here are Shema.org/Product records mapped from Inv_Product
10
+ # data
11
+ def fetch_invoice_details(acumen_client, invoices)
12
+
13
+ invoice_ids = invoices.map { |p| p['identifier'] }
14
+ line_item_data = acumen_client.get_invoice_details(invoice_ids)
15
+ line_item_data = process_invoice_detail_response(line_item_data)
16
+
17
+ return map_line_item_data(invoices, line_item_data)
18
+ end
19
+
20
+ # This function parses the raw data returned from the Prod_Mkt table
21
+ def process_invoice_detail_response(raw_data)
22
+ results = []
23
+ raw_data.each do |invoice_details|
24
+
25
+ begin
26
+ mapped_item = response_mapper(invoice_details, {
27
+ 'Invoice_Detail.Invoice_DETAIL_ID' => 'id',
28
+ 'Invoice_Detail.Title' => 'title',
29
+ 'Invoice_Detail.ProdCode' => 'sku',
30
+ 'Invoice_Detail.Ordered' => 'quantity_ordered',
31
+ 'Invoice_Detail.Ship' => 'quantity_shipped',
32
+ 'Invoice_Detail.BO' => 'quantity_backordered',
33
+ 'Invoice_Detail.BO_Reason' => 'bo_reason',
34
+ 'Invoice_Detail.BO_Prebill' => 'is_prebill',
35
+ 'Invoice_Detail.Invoice_ID' => 'invoice_id',
36
+ 'Invoice_Detail.Modified_Date' => 'modified_date',
37
+ 'Invoice_Detail.BO_Original_Invoice_DETAIL_ID' => 'bo_original_detail_id',
38
+
39
+ })
40
+
41
+ results << mapped_item
42
+ rescue => error
43
+ issue_error(AcumenOrderError.new(
44
+ 'process_invoice_detail_response',
45
+ 'Failed while processing Invoice_Detail record',
46
+ invoice_details,
47
+ error,
48
+ ))
49
+ end
50
+ end
51
+
52
+ results
53
+ end
54
+
55
+ # This function maps parsed Invoice_Detail records to their matching Invoice record
56
+ # and updates the invoice object with the additional data
57
+ def map_line_item_data(invoices, line_item_data)
58
+
59
+ # acceptedOffer
60
+ # identifier
61
+ # sku
62
+ # price
63
+ # priceCurrency
64
+ # name
65
+
66
+ invoices.map do |invoice|
67
+ items = line_item_data.select { |item| item['invoice_id'] == invoice['identifier'] }
68
+
69
+ begin
70
+ unless items.blank?
71
+ invoice['acceptedOffer'] = items.map do |i|
72
+ item = {
73
+ '@type' => 'Offer',
74
+ 'identifier' => i['identifier'],
75
+ 'sku' => i['sku'],
76
+ 'name' => i['title'],
77
+ 'acumenAttributes' => {},
78
+ 'modifiedDate' => i['modified_date'],
79
+ 'invoiceDetailID' => i['id'],
80
+ 'boOriginalDetailId' => i['bo_original_detail_id'],
81
+ }
82
+
83
+ #---------- Acumen Specific Properties ----------#
84
+ item['acumenAttributes']['quantity_ordered'] = i['quantity_ordered']
85
+ item['acumenAttributes']['quantity_shipped'] = i['quantity_shipped']
86
+ item['acumenAttributes']['quantity_backordered'] = i['quantity_backordered']
87
+
88
+ item
89
+ end
90
+ end
91
+ rescue => error
92
+ issue_error(AcumenOrderError.new(
93
+ 'map_line_item_data',
94
+ 'Failed to map line item data for invoice',
95
+ { invoice: invoice, line_items: items },
96
+ error,
97
+ ))
98
+ end
99
+
100
+ invoice
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module is responsible for reading/processing data recrods from the Invoice
4
+ # table in Acumen. This table contains the baseline order/shipment information
5
+ # Shipping method, tracking numbers, etc.
6
+ module InvoiceQueryConcern
7
+ extend AcumenOrderQueryConcern
8
+
9
+ # Fetch/Process the Acumen data,
10
+ def fetch_invoice_data(acumen_client, order_codes)
11
+ invoice_data = acumen_client.get_invoices(order_codes)
12
+
13
+ return process_invoice_response(invoice_data)
14
+ end
15
+
16
+ # This function returns an array of Acumen invoices.
17
+ def process_invoice_response(raw_data)
18
+ raw_data.map do |i|
19
+
20
+ log('------------------------------------------------------------')
21
+ log(":: raw invoice #{i}")
22
+ log('------------------------------------------------------------')
23
+
24
+ invoice = nil
25
+ begin
26
+ invoice = response_mapper(i, {
27
+ 'Invoice.Invoice_ID' => 'identifier',
28
+ 'Invoice.Order_Code' => 'orderNumber',
29
+ 'Invoice.Order_Date' => 'orderDate',
30
+ 'Invoice.Status' => 'orderStatus',
31
+ 'Invoice.Customer_Name' => 'customer',
32
+ 'Invoice.Modified_Date' => 'modified_date',
33
+ })
34
+
35
+ invoice['type'] = '@Order'
36
+
37
+ #---------- Parse Tracking Numbers ----------#
38
+ invoice['orderDelivery'] = []
39
+ tracking_numbers = get_field_value(i, 'Invoice.Tracking_Num')
40
+ tracking_numbers = tracking_numbers.blank? ? [] : tracking_numbers.split(/\s|&#13;/)
41
+
42
+ tracking_numbers.map do |tn|
43
+ invoice['orderDelivery'] << {
44
+ 'type' => '@ParcelDelivery',
45
+ 'trackingNumber' => tn,
46
+ }
47
+ end
48
+
49
+ rescue => error
50
+ issue_error(AcumenOrderError.new(
51
+ 'process_invoice_response',
52
+ 'Failed to load invoice records',
53
+ { raw_data: i },
54
+ error,
55
+ ))
56
+ end
57
+
58
+ log('------------------------------------------------------------')
59
+ log(":: parsed invoice #{invoice}")
60
+ log('------------------------------------------------------------')
61
+
62
+ invoice
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,61 @@
1
+ require 'rails_helper'
2
+ require 'huginn_agent/spec_helper'
3
+ require 'yaml'
4
+
5
+ require_relative '../lib/huginn_acumen_order_agent/acumen_client'
6
+
7
+ spec_folder = File.expand_path(File.dirname(__FILE__))
8
+ mock_data = YAML.load(File.read(spec_folder + "/acumen_product_agent_spec.yml"))
9
+
10
+ def mock_response(ns, ids)
11
+ records = ids.map {|id| mock_data[ns][id]}
12
+ response = <<~TEXT
13
+ <?xml version="1.0" encoding="UTF-8"?>
14
+ <SOAP-ENV:Envelope
15
+ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
16
+ xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
17
+ xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
18
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
19
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
20
+ <SOAP-ENV:Body>
21
+ <acusoapResponse>
22
+ <result_set.#{ns}>#{records.join("\n")}</result_set.#{ns}>
23
+ </acusoapResponse>
24
+ </SOAP-ENV:Body>
25
+ </SOAP-ENV:Envelope>
26
+ TEXT
27
+ response = ::MultiXml.parse(response, {})
28
+ AcumenClient::get_results(response, ns)
29
+ end
30
+
31
+ allow(AcumenClient).to receive(:get_products) do |ids|
32
+ mock_response('Inv_Product', ids)
33
+ end
34
+
35
+ allow(AcumenClient).to receive(:get_products_marketing) do |ids|
36
+ mock_response('ProdMkt', ids)
37
+ end
38
+
39
+ allow(AcumenClient).to receive(:get_linked_products) do |ids|
40
+ mock_response('Product_Link', ids)
41
+ end
42
+
43
+ allow(AcumenClient).to receive(:get_product_contributors) do |ids|
44
+ mock_response('ProdMkt_Contrib_Link', ids)
45
+ end
46
+
47
+ allow(AcumenClient).to receive(:get_product_categories) do |ids|
48
+ mock_response('ProdMkt_WPC', ids)
49
+ end
50
+
51
+
52
+ describe Agents::AcumenProductAgent do
53
+ before(:each) do
54
+ @valid_options = Agents::AcumenProductAgent.new.default_options
55
+ @checker = Agents::AcumenProductAgent.new(:name => "AcumenProductAgent", :options => @valid_options)
56
+ @checker.user = users(:bob)
57
+ @checker.save!
58
+ end
59
+
60
+ pending "add specs here"
61
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: huginn_acumen_order_agent
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jacob Spizziri
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-03-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: huginn_agent
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: The Huginn Acumen Order Agent takes in an array of Order Codes, queries
56
+ the relevant Acumen tables, and emits a set of events with a sane data interface
57
+ for each.
58
+ email:
59
+ - jacob.spizziri@gmail.com
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - LICENSE.txt
65
+ - lib/huginn_acumen_order_agent.rb
66
+ - lib/huginn_acumen_order_agent/acumen_client.rb
67
+ - lib/huginn_acumen_order_agent/acumen_order_agent.rb
68
+ - lib/huginn_acumen_order_agent/acumen_order_error.rb
69
+ - lib/huginn_acumen_order_agent/concerns/acumen_query_concern.rb
70
+ - lib/huginn_acumen_order_agent/concerns/invoice_detail_query_concern.rb
71
+ - lib/huginn_acumen_order_agent/concerns/invoice_query_concern.rb
72
+ - spec/acumen_order_agent_spec.rb
73
+ homepage: https://github.com/5-Stones/huginn_acumen_order_agent
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubygems_version: 3.0.3
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Huginn agent for sane ACUMEN order data.
96
+ test_files:
97
+ - spec/acumen_order_agent_spec.rb