Class: Secretariat::LineItem

Inherits:
Struct
  • Object
show all
Includes:
Versioner
Defined in:
lib/secretariat/line_item.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Versioner

#by_version

Instance Attribute Details

#charge_amountObject

Returns the value of attribute charge_amount



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def charge_amount
  @charge_amount
end

#currency_codeObject

Returns the value of attribute currency_code



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def currency_code
  @currency_code
end

#discount_amountObject

Returns the value of attribute discount_amount



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def discount_amount
  @discount_amount
end

#discount_reasonObject

Returns the value of attribute discount_reason



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def discount_reason
  @discount_reason
end

#gross_amountObject

Returns the value of attribute gross_amount



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def gross_amount
  @gross_amount
end

#nameObject

Returns the value of attribute name



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def name
  @name
end

#net_amountObject

Returns the value of attribute net_amount



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def net_amount
  @net_amount
end

#origin_country_codeObject

Returns the value of attribute origin_country_code



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def origin_country_code
  @origin_country_code
end

#quantityObject

Returns the value of attribute quantity



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def quantity
  @quantity
end

#tax_amountObject

Returns the value of attribute tax_amount



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def tax_amount
  @tax_amount
end

#tax_categoryObject

Returns the value of attribute tax_category



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def tax_category
  @tax_category
end

#tax_percentObject

Returns the value of attribute tax_percent



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def tax_percent
  @tax_percent
end

#unitObject

Returns the value of attribute unit



23
24
25
# File 'lib/secretariat/line_item.rb', line 23

def unit
  @unit
end

Instance Method Details

#errorsObject



42
43
44
# File 'lib/secretariat/line_item.rb', line 42

def errors
  @errors
end

#tax_category_code(version: 2) ⇒ Object



81
82
83
84
85
86
# File 'lib/secretariat/line_item.rb', line 81

def tax_category_code(version: 2)
  if version == 1
    return TAX_CATEGORY_CODES_1[tax_category] || 'S'
  end
  TAX_CATEGORY_CODES[tax_category] || 'S'
end

#to_xml(xml, line_item_index, version: 2, validate: true) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/secretariat/line_item.rb', line 88

def to_xml(xml, line_item_index, version: 2, validate: true)
  net_price = net_amount && BigDecimal(net_amount)
  gross_price = gross_amount && BigDecimal(gross_amount)
  charge_price = charge_amount && BigDecimal(charge_amount)

  if net_price&.zero?
    self.tax_percent = 0
  end
  
  if net_price&.negative?
    # Zugferd doesn't allow negative amounts at the item level.
    # Instead, a negative quantity is used.
    self.quantity = -quantity
    self.gross_amount = gross_price&.abs
    self.net_amount = net_price&.abs
    self.charge_amount = charge_price&.abs
  end

  if validate && !valid?
    raise ValidationError.new("LineItem #{line_item_index} is invalid", errors)
  end

  xml['ram'].IncludedSupplyChainTradeLineItem do
    xml['ram'].AssociatedDocumentLineDocument do
      xml['ram'].LineID line_item_index
    end
    if (version == 2)
      xml['ram'].SpecifiedTradeProduct do
        xml['ram'].Name name
        xml['ram'].OriginTradeCountry do
          xml['ram'].ID origin_country_code
        end
      end
    end
    agreement = by_version(version, 'SpecifiedSupplyChainTradeAgreement', 'SpecifiedLineTradeAgreement')

    xml['ram'].send(agreement) do
      xml['ram'].GrossPriceProductTradePrice do
        Helpers.currency_element(xml, 'ram', 'ChargeAmount', gross_amount, currency_code, add_currency: version == 1, digits: 4)
        if version == 2 && discount_amount
          xml['ram'].BasisQuantity(unitCode: unit_code) do
            xml.text(Helpers.format(quantity, digits: 4))
          end
          xml['ram'].AppliedTradeAllowanceCharge do
            xml['ram'].ChargeIndicator do
              xml['udt'].Indicator 'false'
            end
            Helpers.currency_element(xml, 'ram', 'ActualAmount', discount_amount, currency_code, add_currency: version == 1)
            xml['ram'].Reason discount_reason
          end
        end
        if version == 1 && discount_amount
          xml['ram'].AppliedTradeAllowanceCharge do
            xml['ram'].ChargeIndicator do
              xml['udt'].Indicator 'false'
            end
            Helpers.currency_element(xml, 'ram', 'ActualAmount', discount_amount, currency_code, add_currency: version == 1)
            xml['ram'].Reason discount_reason
          end
        end
      end
      xml['ram'].NetPriceProductTradePrice do
        Helpers.currency_element(xml, 'ram', 'ChargeAmount', net_amount, currency_code, add_currency: version == 1, digits: 4)
        if version == 2
          xml['ram'].BasisQuantity(unitCode: unit_code) do
            xml.text(Helpers.format(quantity, digits: 4))
          end
        end
      end
    end

    delivery = by_version(version, 'SpecifiedSupplyChainTradeDelivery', 'SpecifiedLineTradeDelivery')

    xml['ram'].send(delivery) do
      xml['ram'].BilledQuantity(unitCode: unit_code) do
        xml.text(Helpers.format(quantity, digits: 4))
      end
    end

    settlement = by_version(version, 'SpecifiedSupplyChainTradeSettlement', 'SpecifiedLineTradeSettlement')

    xml['ram'].send(settlement) do
      xml['ram'].ApplicableTradeTax do
        xml['ram'].TypeCode 'VAT'
        xml['ram'].CategoryCode tax_category_code(version: version)

        percent = by_version(version, 'ApplicablePercent', 'RateApplicablePercent')
        xml['ram'].send(percent,Helpers.format(tax_percent))

      end
      monetary_summation = by_version(version, 'SpecifiedTradeSettlementMonetarySummation', 'SpecifiedTradeSettlementLineMonetarySummation')
      xml['ram'].send(monetary_summation) do
        Helpers.currency_element(xml, 'ram', 'LineTotalAmount', (quantity.negative? ? -charge_amount  : charge_amount), currency_code, add_currency: version == 1)
      end
    end

    if version == 1
      xml['ram'].SpecifiedTradeProduct do
        xml['ram'].Name name
      end
    end
  end
end

#unit_codeObject



77
78
79
# File 'lib/secretariat/line_item.rb', line 77

def unit_code
  UNIT_CODES[unit] || 'C62'
end

#valid?Boolean



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/secretariat/line_item.rb', line 46

def valid?
  @errors = []
  net_price = BigDecimal(net_amount)
  gross_price = BigDecimal(gross_amount)
  charge_price = BigDecimal(charge_amount)
  tax = BigDecimal(tax_amount)
  unit_price = net_price * BigDecimal(quantity.abs)

  if charge_price != unit_price
    @errors << "charge price and gross price times quantity deviate: #{charge_price} / #{unit_price}"
    return false
  end
  if discount_amount
    discount = BigDecimal(discount_amount)
    calculated_net_price = (gross_price - discount).round(2, :down)
    if calculated_net_price != net_price
      @errors = "Calculated net price and net price deviate: #{calculated_net_price} / #{net_price}"
      return false
    end
  end

  calculated_tax = charge_price * BigDecimal(tax_percent) / BigDecimal(100)
  calculated_tax = calculated_tax.round(2)
  calculated_tax = -calculated_tax if quantity.negative?
  if calculated_tax != tax
    @errors << "Tax and calculated tax deviate: #{tax} / #{calculated_tax}"
    return false
  end
  return true
end