agris 0.18.0 → 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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/agents/app-explorer.md +212 -0
  3. data/.claude/agents/cross-review.md +152 -0
  4. data/.claude/agents/developer.md +215 -0
  5. data/.claude/agents/pm.md +401 -0
  6. data/.claude/agents/qa.md +235 -0
  7. data/.claude/agents/reviewer.md +185 -0
  8. data/.claude/agents/spec-preflight.md +167 -0
  9. data/.claude/agents/spec.md +244 -0
  10. data/.claude/ai/agent-rules.md +36 -0
  11. data/.claude/ai/spec-writing-guide.md +43 -0
  12. data/.claude/commands/app-explorer.md +87 -0
  13. data/.claude/commands/cross-review.md +91 -0
  14. data/.claude/commands/developer.md +118 -0
  15. data/.claude/commands/pm.md +166 -0
  16. data/.claude/commands/qa.md +111 -0
  17. data/.claude/commands/reviewer.md +86 -0
  18. data/.claude/commands/spec.md +172 -0
  19. data/.github/workflows/ci.yml +62 -0
  20. data/.gitignore +5 -0
  21. data/.rubocop.yml +10 -103
  22. data/.ruby-version +1 -0
  23. data/CHANGELOG.md +79 -0
  24. data/CLAUDE.md +98 -0
  25. data/README.md +9 -1
  26. data/agris.gemspec +3 -2
  27. data/docs/RELEASING.md +147 -0
  28. data/docs/specs/README.md +26 -0
  29. data/docs/specs/accounts-payables/.gitkeep +0 -0
  30. data/docs/specs/accounts-receivables/.gitkeep +0 -0
  31. data/docs/specs/grain/.gitkeep +0 -0
  32. data/docs/specs/inventory/.gitkeep +0 -0
  33. data/docs/specs/messages/.gitkeep +0 -0
  34. data/docs/specs/rename-folder.example.md +82 -0
  35. data/docs/specs/spec-template.md +68 -0
  36. data/docs/specs/support/.gitkeep +0 -0
  37. data/lib/agris/api/accounts_payables/disbursements.rb +19 -0
  38. data/lib/agris/api/accounts_payables/new_disbursement.rb +42 -0
  39. data/lib/agris/api/accounts_payables.rb +3 -0
  40. data/lib/agris/api/accounts_receivables/invoices.rb +1 -1
  41. data/lib/agris/api/accounts_receivables/new_payment.rb +96 -0
  42. data/lib/agris/api/accounts_receivables/new_payment_remark.rb +38 -0
  43. data/lib/agris/api/accounts_receivables/payment_post_result.rb +35 -0
  44. data/lib/agris/api/accounts_receivables/payments.rb +33 -0
  45. data/lib/agris/api/accounts_receivables.rb +6 -0
  46. data/lib/agris/api/document_query_response.rb +6 -2
  47. data/lib/agris/api/grain/contract.rb +3 -3
  48. data/lib/agris/api/grain/new_ticket.rb +1 -0
  49. data/lib/agris/api/grain/purchase_contracts.rb +1 -1
  50. data/lib/agris/api/grain/sales_contracts.rb +1 -1
  51. data/lib/agris/api/grain.rb +4 -2
  52. data/lib/agris/api/inventory/delivery_tickets.rb +1 -1
  53. data/lib/agris/api/inventory/orders.rb +1 -1
  54. data/lib/agris/api/messages/changed_query_base.rb +1 -0
  55. data/lib/agris/api/messages/document_query_base.rb +1 -0
  56. data/lib/agris/api/messages/import.rb +1 -0
  57. data/lib/agris/api/name/new_farm_field.rb +5 -0
  58. data/lib/agris/api/new_voucher.rb +1 -3
  59. data/lib/agris/api/post_result.rb +9 -1
  60. data/lib/agris/client.rb +3 -1
  61. data/lib/agris/version.rb +1 -1
  62. data/lib/agris.rb +1 -0
  63. data/lib/hash.rb +1 -0
  64. metadata +48 -13
  65. data/.circleci/config.yml +0 -45
  66. data/.codeclimate.yml +0 -5
data/docs/RELEASING.md ADDED
@@ -0,0 +1,147 @@
1
+ # Releasing agris
2
+
3
+ The gem is published to [rubygems.org](https://rubygems.org/gems/agris) as `agris`.
4
+
5
+ Releases are cut with `bundle exec rake release`, which tags, pushes and
6
+ publishes in a single irreversible step. Read [What `rake release` actually
7
+ does](#what-rake-release-actually-does) before running it.
8
+
9
+ ## Versioning
10
+
11
+ **One version line, released from `master`.** The version number follows
12
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and describes the
13
+ **API** — not which Ruby the gem supports.
14
+
15
+ Ruby support is expressed in the gemspec instead:
16
+
17
+ ```ruby
18
+ spec.required_ruby_version = '>= 3.1'
19
+ ```
20
+
21
+ Bundler's resolver does the rest. An application on Ruby 3.1 resolves to the
22
+ newest version whose floor it satisfies; one on 3.4 resolves to the newest
23
+ version outright. There is no need for parallel release lines, and consumers do
24
+ not need to pin a version to stay on a supported Ruby.
25
+
26
+ > This replaced an earlier scheme that encoded the supported Ruby in the version
27
+ > number (`0.19.x` for Ruby 3.1, `0.2x` for Ruby 3.3). It fought semver — the
28
+ > version had to step *backwards* at one point — and RubyGems has no concept of
29
+ > release lines anyway: it always serves the highest version as `latest`.
30
+
31
+ ### How Ruby changes map to versions
32
+
33
+ | Change | Version impact |
34
+ |--------|----------------|
35
+ | **Adding** support for a newer Ruby (3.5, 4.x) | **None.** Add it to the CI matrix. Patch release only if fixes were needed. |
36
+ | **Dropping** support for an older Ruby (raising `required_ruby_version`) | **Breaking** — major bump. |
37
+
38
+ Everything else is ordinary semver on the public API.
39
+
40
+ Raising the floor is breaking because applications that could install the gem
41
+ yesterday cannot today. Do it deliberately, in its own release.
42
+
43
+ ### Keep the floor honest
44
+
45
+ `.github/workflows/ci.yml` runs the suite across **floor, current and next** —
46
+ currently 3.1, 3.3 and 3.4. If you change `required_ruby_version`, change the
47
+ matrix in the same PR, or the declared floor becomes a claim nothing tests.
48
+
49
+ Note that the matrix job names are required status checks on `master`
50
+ (`test (3.1)`, `test (3.3)`, `test (3.4)`, `lint`). Changing the matrix changes
51
+ those names, so branch protection must be updated to match — otherwise every
52
+ PR blocks on a check that can never report.
53
+
54
+ ## Pre-release checklist
55
+
56
+ 1. **Be on `master`**, up to date, with everything merged that you intend to
57
+ ship. `rake release` tags and pushes whatever branch you are standing on —
58
+ it does not check.
59
+ 2. **Working tree is clean.** `rake release` aborts otherwise.
60
+ 3. **Full suite is green:**
61
+ ```sh
62
+ bundle exec rake # specs + rubocop
63
+ ```
64
+ 4. **Version is bumped** in `lib/agris/version.rb`, and the bump is committed.
65
+ 5. **`required_ruby_version` is correct** for what you are shipping, and the CI
66
+ matrix covers it.
67
+ 6. **The tag does not already exist:**
68
+ ```sh
69
+ git tag --list 'v*' | sort -V | tail
70
+ ```
71
+ 7. **RubyGems credentials are present** — see [Credentials](#credentials).
72
+
73
+ ## Cutting the release
74
+
75
+ ```sh
76
+ git checkout master
77
+ git pull
78
+ # bump lib/agris/version.rb, commit, and merge via PR
79
+ bundle exec rake # confirm green
80
+ bundle exec rake release
81
+ ```
82
+
83
+ If your account has MFA enabled, `gem push` prompts for an OTP code partway
84
+ through — do not walk away from the terminal.
85
+
86
+ ## What `rake release` actually does
87
+
88
+ Provided by `bundler/gem_tasks`. In order:
89
+
90
+ 1. **Guards the working tree** — aborts with "There are files that need to be
91
+ committed first" if anything is modified or staged.
92
+ 2. **Checks whether `v<version>` is already tagged** — if so, skips tagging and
93
+ continues to publish anyway.
94
+ 3. **Creates the tag** — `git tag -m "Version <version>" v<version>`.
95
+ 4. **Builds the gem** into `pkg/agris-<version>.gem`.
96
+ 5. **Pushes to git** — the **current branch** and the new tag, to that branch's
97
+ configured remote (`origin` by default).
98
+ 6. **Publishes to rubygems.org** — `gem push pkg/agris-<version>.gem`.
99
+
100
+ Steps 5 and 6 reach outside the repository. Step 6 is effectively permanent.
101
+
102
+ If tagging succeeds but a later step fails, the tag is removed automatically.
103
+ If the *push* succeeds and publishing fails, the tag is already public — delete
104
+ it manually before retrying:
105
+
106
+ ```sh
107
+ git tag -d v<version>
108
+ git push origin :refs/tags/v<version>
109
+ ```
110
+
111
+ ## Credentials
112
+
113
+ `gem push` reads `~/.gem/credentials`. If you have never pushed from this
114
+ machine:
115
+
116
+ ```sh
117
+ gem signin
118
+ ```
119
+
120
+ The gemspec sets no `allowed_push_host`, so pushes go to rubygems.org. You must
121
+ be an owner of the `agris` gem — check with `gem owner agris`.
122
+
123
+ ## After releasing
124
+
125
+ ```sh
126
+ gem list agris --remote --all # new version is listed
127
+ git ls-remote --tags origin # tag reached the remote
128
+ ```
129
+
130
+ Confirm the published gem declares the Ruby floor you expect:
131
+
132
+ ```sh
133
+ gem specification agris -r required_ruby_version
134
+ ```
135
+
136
+ ## If a release goes wrong
137
+
138
+ A published version can be yanked, but the version number is **burned
139
+ permanently** — it can never be reused:
140
+
141
+ ```sh
142
+ gem yank agris -v <version>
143
+ ```
144
+
145
+ Yanking removes it from resolution for new installs; it does not recall it from
146
+ anyone who already has it. Prefer releasing a fixed higher version over yanking
147
+ wherever possible.
@@ -0,0 +1,26 @@
1
+ # Specs
2
+
3
+ Source of truth for behavior in the `agris` gem. Implementation, tests, and PRs trace back to spec IDs (R#, E#, AC-#, AT#).
4
+
5
+ ## How to add a spec
6
+ 1. Copy `spec-template.md` into the appropriate area folder (see below).
7
+ 2. Name the file by feature, kebab-case (e.g., `import-grain-ticket.md`).
8
+ 3. Fill in Goal → Rules → Acceptance Criteria → Acceptance Tests.
9
+ 4. Reference spec IDs in commits, tests, and PR descriptions.
10
+
11
+ ## Layout
12
+
13
+ Specs are grouped by Agris API area, mirroring `lib/agris/api/`:
14
+
15
+ - `grain/` — tickets, contracts (purchase/sales), commodity codes, grade factors, rates
16
+ - `inventory/` — delivery tickets, orders
17
+ - `accounts-payables/` — voucher posting
18
+ - `accounts-receivables/` — invoice posting
19
+ - `messages/` — document-tracking and "changed since" queries
20
+ - `support/` — support / utility messages
21
+
22
+ Cross-cutting specs (config, transport, error model) can live at the top level of `docs/specs/`.
23
+
24
+ ## Templates
25
+ - `spec-template.md` — blank template, copy this.
26
+ - `rename-folder.example.md` — completed example showing the expected level of detail.
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,82 @@
1
+ # Spec: Rename Folder (Example)
2
+
3
+ ## Goal
4
+ Allow a user to rename an existing folder.
5
+
6
+ ## Non Goals
7
+ - Moving folders
8
+ - Renaming multiple folders
9
+
10
+ ## Definitions
11
+ - Folder: user-owned container for documents.
12
+
13
+ ## Interfaces
14
+ Endpoint:
15
+ `PATCH /folders/{id}`
16
+
17
+ Input:
18
+ ```json
19
+ {
20
+ "name": "string"
21
+ }
22
+ ```
23
+
24
+ Output:
25
+ ```json
26
+ {
27
+ "id": "string",
28
+ "name": "string"
29
+ }
30
+ ```
31
+
32
+ ## Rules
33
+ R1: Folder name must be 1-80 characters.
34
+ R2: Leading and trailing whitespace must be trimmed.
35
+ R3: If name is unchanged, return success without modification.
36
+ R4: User must have folder write permission.
37
+
38
+ ## State Model
39
+ - Existing folder name -> renamed folder name (or unchanged for idempotent update).
40
+
41
+ ## Edge Cases
42
+ E1: Folder does not exist -> return 404.
43
+ E2: Invalid name -> return 422.
44
+ E3: Unauthorized -> return 403.
45
+
46
+ ## Acceptance Criteria
47
+ AC-1: User can rename an existing folder with a valid name (1-80 chars).
48
+ AC-2: System trims whitespace and handles idempotent renames.
49
+ AC-3: Unauthorized or invalid requests are rejected with appropriate status codes.
50
+
51
+ ## Acceptance Tests
52
+ AT1
53
+ Given a valid folder id
54
+ When a valid name is submitted
55
+ Then the folder name is updated.
56
+ Covers: R1, R2
57
+
58
+ AT2
59
+ Given an unchanged folder name
60
+ When request is submitted
61
+ Then return success without modification.
62
+ Covers: R3
63
+
64
+ AT3
65
+ Given a user without permission
66
+ When request is submitted
67
+ Then return 403.
68
+ Covers: R4
69
+
70
+ ## Observability
71
+ - Log validation failures with request id and rule ID.
72
+ - Emit metric for rename success/failure counts.
73
+
74
+ ## Implementation Decisions
75
+
76
+ | Date | Decision | Rationale |
77
+ |------|----------|-----------|
78
+
79
+ ## Change Log
80
+
81
+ | Date | Change | Affected IDs | Rationale |
82
+ |------|--------|-------------|-----------|
@@ -0,0 +1,68 @@
1
+ <!-- See rename-folder.example.md for a completed example of this template -->
2
+
3
+ # Spec: <Feature Name>
4
+
5
+ ## Goal
6
+ Short description of the feature's purpose and value.
7
+
8
+ ## Non Goals
9
+ Explicitly excluded behavior. List what this spec intentionally does NOT cover.
10
+
11
+ ## Definitions
12
+ Key domain terms used in this spec. Define any term a new team member might misunderstand.
13
+
14
+ ## Interfaces
15
+ Inputs, outputs, APIs, or UI events. Include request/response schemas where applicable.
16
+
17
+ ## Rules
18
+ <!-- Rules describe system behavior at implementation level.
19
+ Each rule should be specific enough to test.
20
+ Bad: "User can log in."
21
+ Good: "POST /api/auth/login with valid credentials returns 200 and a session token (R1)." -->
22
+
23
+ R1: <rule>
24
+ R2: <rule>
25
+
26
+ ## State Model
27
+ If the system has state transitions, describe them here. Otherwise remove this section.
28
+
29
+ ## Edge Cases
30
+ E1: <edge case and expected behavior>
31
+ E2: <edge case and expected behavior>
32
+
33
+ ## Acceptance Criteria
34
+ <!-- ACs describe observable outcomes a stakeholder can verify.
35
+ Rules are implementation-facing; ACs are user-facing.
36
+ Example: "AC-1: User sees a green checkmark when password meets all strength requirements." -->
37
+
38
+ AC-1: <criterion>
39
+ AC-2: <criterion>
40
+
41
+ ## Acceptance Tests
42
+ <!-- Each AT must have a Covers: field listing the R# or E# it validates.
43
+ An AT can cover multiple rules. -->
44
+
45
+ AT1
46
+ Given <context>
47
+ When <action>
48
+ Then <outcome>
49
+ Covers: R1
50
+
51
+ AT2
52
+ Given <context>
53
+ When <action>
54
+ Then <outcome>
55
+ Covers: R2
56
+
57
+ ## Observability
58
+ Logs, metrics, alerts (optional).
59
+
60
+ ## Implementation Decisions
61
+
62
+ | Date | Decision | Rationale |
63
+ |------|----------|-----------|
64
+
65
+ ## Change Log
66
+
67
+ | Date | Change | Affected IDs | Rationale |
68
+ |------|--------|-------------|-----------|
File without changes
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agris
4
+ module Api
5
+ module AccountsPayables
6
+ module Disbursements
7
+ def create_disbursement(disbursement)
8
+ response = @request.process_message(
9
+ Gyoku.xml(xml: context_hash),
10
+ 82_320,
11
+ create_post_payload_xml([disbursement.to_xml_hash])
12
+ )
13
+
14
+ PostResult.new(response)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agris
4
+ module Api
5
+ module AccountsPayables
6
+ class NewDisbursement
7
+ include XmlModel
8
+
9
+ ATTRIBUTE_NAMES = %w(
10
+ applied_date
11
+ apply_payment_on_account
12
+ bank_code
13
+ cash_source
14
+ check_location
15
+ check_number
16
+ currency_code
17
+ discount_amount
18
+ exchange_rate
19
+ exchange_rate_date
20
+ misc_reference
21
+ name_id
22
+ payment_amount
23
+ payment_date
24
+ prepayment_reference
25
+ prepayment_type
26
+ record_type
27
+ reserved
28
+ voucher_location
29
+ voucher_number
30
+ ).freeze
31
+
32
+ attr_reader(*ATTRIBUTE_NAMES)
33
+
34
+ def initialize(hash = {})
35
+ super
36
+
37
+ @record_type = 'ACPD0'
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -3,6 +3,9 @@
3
3
  module Agris
4
4
  module Api
5
5
  module AccountsPayables
6
+ autoload :Disbursements, 'agris/api/accounts_payables/disbursements'
7
+ autoload :NewDisbursement, 'agris/api/accounts_payables/new_disbursement'
8
+
6
9
  module Vouchers
7
10
  def create_voucher(voucher)
8
11
  response = @request.process_message(
@@ -17,7 +17,7 @@ module Agris
17
17
  )
18
18
  end
19
19
 
20
- def invoices_changed_since(datetime, detail = false)
20
+ def invoices_changed_since(datetime, detail: false)
21
21
  extract_documents(
22
22
  Messages::QueryChangedInvoices.new(datetime, detail),
23
23
  Agris::Api::AccountsReceivables::Invoice
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agris
4
+ module Api
5
+ module AccountsReceivables
6
+ class NewPayment
7
+ include XmlModel
8
+
9
+ RECEIVE_ON_ACCOUNT = '0'
10
+ APPLY_TO_INVOICE = 'E'
11
+
12
+ ATTRIBUTE_NAMES = %w(
13
+ applied_date
14
+ apply_receive_on_acct
15
+ bank_code
16
+ cash_source
17
+ check_number
18
+ discount_amount
19
+ invoice_line_pricing_no
20
+ invoice_location
21
+ name_id
22
+ payment_amount
23
+ payment_date
24
+ receipt_location
25
+ receipt_number
26
+ record_type
27
+ ).freeze
28
+
29
+ attr_reader(*ATTRIBUTE_NAMES)
30
+
31
+ # Agris allocates the receipt number, so it is left for the response.
32
+ # An applied date or invoice on an unapplied receipt is rejected.
33
+ def self.receive(attributes = {})
34
+ new(
35
+ attributes.merge(
36
+ apply_receive_on_acct: RECEIVE_ON_ACCOUNT,
37
+ applied_date: '',
38
+ invoice_line_pricing_no: ''
39
+ )
40
+ )
41
+ end
42
+
43
+ # Draws down the unapplied balance of a receipt Agris already
44
+ # allocated; it cannot create one.
45
+ def self.apply(attributes = {})
46
+ new(attributes.merge(apply_receive_on_acct: APPLY_TO_INVOICE))
47
+ end
48
+
49
+ def initialize(attributes = {})
50
+ super
51
+
52
+ @record_type = 'ACRR0'
53
+ @remarks = []
54
+ end
55
+
56
+ def add_remark(remark)
57
+ @remarks << remark
58
+
59
+ self
60
+ end
61
+
62
+ def remarks
63
+ @remarks || []
64
+ end
65
+
66
+ def records
67
+ [self] + remarks
68
+ end
69
+
70
+ def for_allocation(allocation)
71
+ allocated = self.class.new(hash.merge(allocation_hash(allocation)))
72
+
73
+ remarks.each do |remark|
74
+ allocated.add_remark(remark.for_allocation(allocation))
75
+ end
76
+
77
+ allocated
78
+ end
79
+
80
+ def xml_ignore_attributes
81
+ [:remarks]
82
+ end
83
+
84
+ protected
85
+
86
+ def allocation_hash(allocation)
87
+ {
88
+ bank_code: allocation.bank_code,
89
+ receipt_location: allocation.receipt_location,
90
+ receipt_number: allocation.receipt_number
91
+ }
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agris
4
+ module Api
5
+ module AccountsReceivables
6
+ class NewPaymentRemark
7
+ include XmlModel
8
+
9
+ ATTRIBUTE_NAMES = %w(
10
+ bank_code
11
+ receipt_location
12
+ receipt_number
13
+ record_type
14
+ remark_number
15
+ remark_value
16
+ ).freeze
17
+
18
+ attr_reader(*ATTRIBUTE_NAMES)
19
+
20
+ def initialize(attributes = {})
21
+ super
22
+
23
+ @record_type = 'ACRR1'
24
+ end
25
+
26
+ def for_allocation(allocation)
27
+ self.class.new(
28
+ hash.merge(
29
+ bank_code: allocation.bank_code,
30
+ receipt_location: allocation.receipt_location,
31
+ receipt_number: allocation.receipt_number
32
+ )
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agris
4
+ module Api
5
+ module AccountsReceivables
6
+ # The posted receipt comes back in `document` as a fixed-width triple:
7
+ # receipt location (3), receipt number (6), bank code (2).
8
+ class PaymentPostResult < PostResult
9
+ def receipt_location
10
+ document_field(0, 3)
11
+ end
12
+
13
+ def receipt_number
14
+ document_field(3, 6)
15
+ end
16
+
17
+ def bank_code
18
+ document_field(9, 2)
19
+ end
20
+
21
+ # A rejected post still returns the location and bank code, so only the
22
+ # receipt number tells you whether Agris allocated a receipt.
23
+ def allocated?
24
+ !receipt_number.empty?
25
+ end
26
+
27
+ protected
28
+
29
+ def document_field(offset, length)
30
+ document_number.to_s[offset, length].to_s.strip
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agris
4
+ module Api
5
+ module AccountsReceivables
6
+ module Payments
7
+ def create_payment(payment)
8
+ post_payment(payment)
9
+ end
10
+
11
+ # Agris will not accept a receipt and its applications in one message,
12
+ # so each application posts against the receipt Agris allocated.
13
+ def apply_payment(payment, allocation)
14
+ post_payment(payment.for_allocation(allocation))
15
+ end
16
+
17
+ protected
18
+
19
+ def post_payment(payment)
20
+ message = Messages::Import.new(payment)
21
+
22
+ response = @request.process_message(
23
+ Gyoku.xml(xml: context_hash),
24
+ message.message_number,
25
+ message.to_xml
26
+ )
27
+
28
+ PaymentPostResult.new(response)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -4,6 +4,12 @@ module Agris
4
4
  module AccountsReceivables
5
5
  autoload :Invoice, 'agris/api/accounts_receivables/invoice'
6
6
  autoload :Invoices, 'agris/api/accounts_receivables/invoices'
7
+ autoload :NewPayment, 'agris/api/accounts_receivables/new_payment'
8
+ autoload :NewPaymentRemark,
9
+ 'agris/api/accounts_receivables/new_payment_remark'
10
+ autoload :PaymentPostResult,
11
+ 'agris/api/accounts_receivables/payment_post_result'
12
+ autoload :Payments, 'agris/api/accounts_receivables/payments'
7
13
  autoload :SpecificInvoiceExtract,
8
14
  'agris/api/accounts_receivables/specific_invoice_extract'
9
15
  end
@@ -9,7 +9,8 @@ module Agris
9
9
  end
10
10
 
11
11
  def last_request_date_time
12
- Time.parse(@output_hash[pluralized_resource_name]['system']['lastrequestdatetime'])
12
+ system_hash = @output_hash[pluralized_resource_name]['system']
13
+ Time.parse(system_hash['lastrequestdatetime'])
13
14
  end
14
15
 
15
16
  def documents
@@ -37,7 +38,10 @@ module Agris
37
38
  end
38
39
 
39
40
  def resource_type
40
- @resource_type ||= Object.const_get(self.class.name.split('::').last.chomp('ExtractResponse'))
41
+ @resource_type ||= begin
42
+ name = self.class.name.split('::').last.chomp('ExtractResponse')
43
+ Object.const_get(name)
44
+ end
41
45
  end
42
46
 
43
47
  def resources
@@ -104,11 +104,11 @@ module Agris
104
104
  def self.add_transcodes_to_schedule(schedule)
105
105
  if schedule['trancodes']
106
106
  trancodes = schedule['trancodes']['trancode']
107
- trancodes = [trancodes] unless trancodes.class == Array
107
+ trancodes = [trancodes] unless trancodes.instance_of?(Array)
108
108
  trancodes.each do |trancode|
109
- label_code = trancode['label'].downcase + 'code'
109
+ label_code = "#{trancode['label'].downcase}code"
110
110
  schedule[label_code] = trancode['code']
111
- label_description = trancode['label'].downcase + 'description'
111
+ label_description = "#{trancode['label'].downcase}description"
112
112
  schedule[label_description] = trancode['description']
113
113
  end
114
114
  end
@@ -31,6 +31,7 @@ module Agris
31
31
  tare_weight
32
32
  freight_weight
33
33
  freight_rate
34
+ freight_uom
34
35
  additional_freight
35
36
  cash_price
36
37
  cash_basis