Class: Twinfield::SalesInvoice

Inherits:
AbstractModel show all
Extended by:
Helpers::Parsers
Defined in:
lib/twinfield/sales_invoice.rb

Defined Under Namespace

Classes: Financials, Line, VatLine

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers::Parsers

parse_date, parse_datetime, parse_float

Constructor Details

#initialize(invoicetype:, duedate: nil, invoicedate: nil, performancedate: nil, bank: nil, invoiceaddressnumber: nil, deliveraddressnumber: nil, customer: nil, customer_code: nil, period: nil, currency: nil, status: "concept", paymentmethod: nil, headertext: nil, footertext: nil, office: nil, invoicenumber: nil, financials: {}, lines: [], vat_lines: []) ⇒ SalesInvoice

Returns a new instance of SalesInvoice.

Raises:

  • (ArgumentError)


249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/twinfield/sales_invoice.rb', line 249

def initialize(invoicetype:, duedate: nil, invoicedate: nil, performancedate: nil, bank: nil, invoiceaddressnumber: nil, deliveraddressnumber: nil, customer: nil, customer_code: nil, period: nil, currency: nil, status: "concept", paymentmethod: nil, headertext: nil, footertext: nil, office: nil, invoicenumber: nil, financials: {}, lines: [], vat_lines: [])
  self.lines = lines.collect { |a| SalesInvoice::Line.new(**a.to_h) }
  self.vat_lines = vat_lines.collect { |a| SalesInvoice::VatLine.new(**a.to_h) }
  self.financials = SalesInvoice::Financials.new(**financials.to_h) if financials && financials != {}
  @invoicetype = invoicetype
  @invoicedate = invoicedate.is_a?(String) ? Date.parse(invoicedate) : invoicedate
  @duedate = duedate.is_a?(String) ? Date.parse(duedate) : duedate
  @performancedate = performancedate.is_a?(String) ? Date.parse(performancedate) : performancedate
  @bank = bank
  @invoiceaddressnumber = invoiceaddressnumber
  @deliveraddressnumber = deliveraddressnumber
  self.customer = customer || customer_code
  raise ArgumentError.new("missing keyword: :customer or :customer_code") unless self.customer_code
  @period = period
  @currency = currency
  @status = status
  @paymentmethod = paymentmethod
  @headertext = headertext
  @footertext = footertext
  @office = office || Twinfield.configuration.company
  @invoicenumber = invoicenumber
end

Instance Attribute Details

#bankObject

Returns the value of attribute bank.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def bank
  @bank
end

#currencyObject

Returns the value of attribute currency.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def currency
  @currency
end

#customer_codeObject

Returns the value of attribute customer_code.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def customer_code
  @customer_code
end

#deliveraddressnumberObject

Returns the value of attribute deliveraddressnumber.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def deliveraddressnumber
  @deliveraddressnumber
end

#duedateObject

Returns the value of attribute duedate.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def duedate
  @duedate
end

#financialsObject

Returns the value of attribute financials.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def financials
  @financials
end

#footertextObject

Returns the value of attribute footertext.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def footertext
  @footertext
end

#headertextObject

Returns the value of attribute headertext.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def headertext
  @headertext
end

#invoiceaddressnumberObject

Returns the value of attribute invoiceaddressnumber.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def invoiceaddressnumber
  @invoiceaddressnumber
end

#invoicedateObject

Returns the value of attribute invoicedate.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def invoicedate
  @invoicedate
end

#invoicenumberObject

Returns the value of attribute invoicenumber.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def invoicenumber
  @invoicenumber
end

#invoicetypeObject

Returns the value of attribute invoicetype.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def invoicetype
  @invoicetype
end

#linesObject

Returns the value of attribute lines.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def lines
  @lines
end

#officeObject

Returns the value of attribute office.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def office
  @office
end

#paymentmethodObject

Returns the value of attribute paymentmethod.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def paymentmethod
  @paymentmethod
end

#performancedateObject

Returns the value of attribute performancedate.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def performancedate
  @performancedate
end

#periodObject

Returns the value of attribute period.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def period
  @period
end

#statusObject

Returns the value of attribute status.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def status
  @status
end

#valueincObject

Returns the value of attribute valueinc.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def valueinc
  @valueinc
end

#vat_linesObject

Returns the value of attribute vat_lines.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def vat_lines
  @vat_lines
end

#vatvalueObject

Returns the value of attribute vatvalue.



247
248
249
# File 'lib/twinfield/sales_invoice.rb', line 247

def vatvalue
  @vatvalue
end

Class Method Details

.find(invoicenumber, invoicetype:) ⇒ Object



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
# File 'lib/twinfield/sales_invoice.rb', line 116

def find(invoicenumber, invoicetype:)
  options = {office: Twinfield.configuration.company, code: invoicetype, invoicenumber: invoicenumber}

  invoice_xml = Twinfield::Api::Process.read(:salesinvoice, options)

  invoice = Twinfield::SalesInvoice.new(
    office: invoice_xml.css("header office").text,
    invoicetype: invoice_xml.css("header invoicetype").text,
    invoicedate: parse_date(invoice_xml.css("header invoicedate").text),
    duedate: parse_date(invoice_xml.css("header duedate").text),
    bank: invoice_xml.css("header bank").text,
    deliveraddressnumber: invoice_xml.css("header deliveraddressnumber").text&.to_i,
    invoiceaddressnumber: invoice_xml.css("header invoiceaddressnumber").text&.to_i,
    customer: invoice_xml.css("header customer").text,
    period: invoice_xml.css("header period").text,
    currency: invoice_xml.css("header currency").text,
    status: invoice_xml.css("header status").text,
    paymentmethod: invoice_xml.css("header paymentmethod").text,
    headertext: invoice_xml.css("header headertext").text,
    footertext: invoice_xml.css("header footertext").text
  )

  invoice.invoicenumber = invoice_xml.css("header invoicenumber").text

  return nil if invoice.invoicenumber.strip != invoicenumber.to_s

  invoice.financials = Financials.new(code: invoice_xml.css("financials code").text, number: invoice_xml.css("financials number").text)

  invoice_xml.css("lines line").each do |xml_line|
    line = Line.new(
      id: xml_line.attributes["id"].text,
      article: xml_line.css("article").text,
      subarticle: xml_line.css("subarticle").text,
      quantity: parse_float(xml_line.css("quantity").text),
      units: xml_line.css("units").text,
      allowdiscountorpremium: xml_line.css("allowdiscountorpremium").text,
      description: xml_line.css("description").text,
      unitspriceexcl: parse_float(xml_line.css("unitspriceexcl").text),
      freetext1: xml_line.css("freetext1").text,
      freetext2: xml_line.css("freetext2").text,
      freetext3: xml_line.css("freetext3").text,
      dim1: xml_line.css("dim1").text,
      vatcode: xml_line.css("vatcode").text
    )
    line.valueexcl = (xml_line.css("valueexcl").text == "") ? nil : xml_line.css("valueexcl").text.to_f
    line.vatvalue = (xml_line.css("vatvalue").text == "") ? nil : xml_line.css("vatvalue").text.to_f
    line.valueinc = (xml_line.css("valueinc").text == "") ? nil : xml_line.css("valueinc").text.to_f
    invoice.lines << line
  end

  invoice_xml.css("vatlines vatline").each do |xml_line|
    line = VatLine.new(
      vatcode: xml_line.css("vatcode").text,
      vatname: xml_line.css("vatcode")[0].attributes["name"].text,
      vatvalue: parse_float(xml_line.css("vatvalue").text),
      performancetype: xml_line.css("performancetype").text,
      performancedate: xml_line.css("performancedate").text
    )

    invoice.vat_lines << line
  end

  invoice
end

.search(options = {}) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/twinfield/sales_invoice.rb', line 181

def search(options = {})
  Twinfield::Api::Process.request(:process_xml_string) do
    %(
      <columns code="000">
       <sort>
          <field>fin.trs.head.code</field>
       </sort>
       <column>
          <field>fin.trs.head.yearperiod</field>
          <label>Period</label>
          <visible>true</visible>
          <ask>true</ask>
          <operator>between</operator>
          <from>2021/01</from>
          <to>2021/12</to>
       </column>
       <column>
          <field>fin.trs.head.code</field>
          <label>Transaction type</label>
          <visible>true</visible>
       </column>
       <column>
          <field>fin.trs.head.shortname</field>
          <label>Name</label>
          <visible>true</visible>
       </column>
       <column>
          <field>fin.trs.head.number</field>
          <label>Trans. no.</label>
          <visible>true</visible>
       </column>
       <column>
          <field>fin.trs.line.dim1</field>
          <label>General ledger</label>
          <visible>true</visible>
          <ask>true</ask>
          <operator>between</operator>
          <from>1300</from>
          <to>1300</to>
       </column>
       <column>
          <field>fin.trs.head.curcode</field>
          <label>Currency</label>
          <visible>true</visible>
       </column>
       <column>
          <field>fin.trs.line.valuesigned</field>
          <label>Value</label>
          <visible>true</visible>
       </column>
       <column>
          <field>fin.trs.line.description</field>
          <label>Description</label>
          <visible>true</visible>
       </column>

     </columns>
    )
    # <column>
    #   <field>fin.trs.line.dim2</field>
    #   <label>Debtor</label><visible>true</visible><from>#{code}</from><to>#{code}</to><operator>between</operator>
    # </column>
  end
end

Instance Method Details

#associated_linesObject



272
273
274
275
276
277
# File 'lib/twinfield/sales_invoice.rb', line 272

def associated_lines
  lines.map { |a|
    a.invoice = self
    a
  }
end

#autobalancevatObject



283
284
285
# File 'lib/twinfield/sales_invoice.rb', line 283

def autobalancevat
  @autobalancevat || true
end

#customerObject



308
309
310
# File 'lib/twinfield/sales_invoice.rb', line 308

def customer
  @customer ||= Twinfield::Customer.find(customer_code)
end

#customer=(customer) ⇒ Object



299
300
301
302
303
304
305
306
# File 'lib/twinfield/sales_invoice.rb', line 299

def customer= customer
  if customer.is_a?(String) || customer.is_a?(Numeric)
    @customer_code = customer.to_i
  elsif customer.is_a? Twinfield::Customer
    @customer_code = customer.code
    @customer = customer
  end
end

#deliver_addressObject



312
313
314
# File 'lib/twinfield/sales_invoice.rb', line 312

def deliver_address
  customer.addresses[deliveraddressnumber - 1] if deliveraddressnumber
end

#final?Boolean

Returns:

  • (Boolean)


295
296
297
# File 'lib/twinfield/sales_invoice.rb', line 295

def final?
  ["final", :final].include? status
end

#generate_linesObject



287
288
289
290
291
292
293
# File 'lib/twinfield/sales_invoice.rb', line 287

def generate_lines
  line_id = 0
  lines.map do |line|
    line_id += 1
    line.to_xml(line_id)
  end
end

#invoice_addressObject



316
317
318
# File 'lib/twinfield/sales_invoice.rb', line 316

def invoice_address
  customer.addresses[invoiceaddressnumber - 1] if invoiceaddressnumber
end

#raisewarningObject



279
280
281
# File 'lib/twinfield/sales_invoice.rb', line 279

def raisewarning
  @raisewarning || false
end

#saveObject



390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/twinfield/sales_invoice.rb', line 390

def save
  response = Twinfield::Api::Process.request do
    to_xml
  end

  xml = Nokogiri::XML(response.body[:process_xml_string_response][:process_xml_string_result])

  if xml.at_css("salesinvoice").attributes["result"].value == "1"
    self.invoicenumber = xml.at_css("invoicenumber").content
    self
  elsif xml.css("header status").text == "final"
    raise Twinfield::Create::Finalized.new(xml.css("[msg]").map { |x| x.attributes["msg"].value }.join(" "), object: self)
  elsif lines.count == 0
    raise Twinfield::Create::EmptyInvoice.new(xml.css("[msg]").map { |x| x.attributes["msg"].value }.join(" "), object: self)
  else
    raise Twinfield::Create::Error.new(xml.css("[msg]").map { |x| x.attributes["msg"].value }.join(" "), object: self)
  end
end

#to_hObject



366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/twinfield/sales_invoice.rb', line 366

def to_h
  {
    lines: lines.collect(&:to_h),
    vat_lines: vat_lines.collect(&:to_h),
    financials: financials&.to_h,
    invoicetype: invoicetype,
    invoicedate: invoicedate,
    duedate: duedate,
    performancedate: performancedate,
    bank: bank,
    invoiceaddressnumber: invoiceaddressnumber,
    deliveraddressnumber: deliveraddressnumber,
    customer_code: customer_code,
    period: period,
    currency: currency,
    status: status,
    paymentmethod: paymentmethod,
    headertext: headertext,
    footertext: footertext,
    office: office || Twinfield.configuration.company,
    invoicenumber: invoicenumber
  }
end

#to_xmlObject



336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/twinfield/sales_invoice.rb', line 336

def to_xml
  Nokogiri::XML::Builder.new do |xml|
    xml.salesinvoice(raisewarning: raisewarning, autobalancevat: autobalancevat) {
      xml.header do
        xml.office office
        xml.invoicenumber invoicenumber if invoicenumber
        xml.invoicetype invoicetype
        xml.invoicedate invoicedate&.strftime("%Y%m%d")
        xml.duedate duedate&.strftime("%Y%m%d")
        xml.performancedate performancedate.strftime("%Y%m%d") if performancedate
        xml.bank bank
        xml.invoiceaddressnumber invoiceaddressnumber if invoiceaddressnumber
        xml.deliveraddressnumber deliveraddressnumber if deliveraddressnumber
        xml.customer customer_code
        xml.period period if period
        xml.currency currency
        xml.status status
        xml.paymentmethod paymentmethod
        xml.headertext headertext
        xml.footertext footertext
      end
      xml.lines do
        generate_lines.each do |line|
          xml << line
        end
      end
    }
  end.doc.root.to_xml
end

#totalexclObject

helper method to calculate a total excl price



332
333
334
# File 'lib/twinfield/sales_invoice.rb', line 332

def totalexcl
  lines.map(&:valueexcl).compact.sum
end

#totalincObject Also known as: total

helper method to calculate a total price



325
326
327
# File 'lib/twinfield/sales_invoice.rb', line 325

def totalinc
  lines.map(&:valueinc).compact.sum
end

#transactionObject



320
321
322
# File 'lib/twinfield/sales_invoice.rb', line 320

def transaction
  @transaction ||= financials&.number ? Twinfield::Browse::Transaction::Customer.find(number: financials.number, code: financials.code) : nil
end