diff --git a/lib/syspro.rb b/lib/syspro.rb index 70de608..ee4cc4e 100644 --- a/lib/syspro.rb +++ b/lib/syspro.rb @@ -34,11 +34,12 @@ require 'syspro/business_objects/portor' require 'syspro/business_objects/portoi' require 'syspro/business_objects/porqry' require 'syspro/business_objects/comsfm' +require 'syspro/business_objects/invqry' require 'syspro/business_objects/models/sor' require 'syspro/business_objects/models/sor_detail' require 'syspro/business_objects/models/por_detail' - +require 'syspro/business_objects/models/inv_qry_options' require 'syspro/business_objects/models/purchase_order' require 'syspro/business_objects/models/purchase_orders/header' require 'syspro/business_objects/models/purchase_orders/order_details' @@ -46,8 +47,8 @@ require 'syspro/business_objects/models/purchase_orders/stock_line' require 'syspro/business_objects/models/purchase_orders/freight_line' require 'syspro/business_objects/models/purchase_orders/misc_charge_line' require 'syspro/business_objects/models/purchase_orders/comment_line' - require 'syspro/business_objects/models/comsfm_item' +require 'syspro/business_objects/models/inv' require 'syspro/business_objects/parsers/combrw_parser' require 'syspro/business_objects/parsers/comfch_parser' @@ -56,6 +57,7 @@ require 'syspro/business_objects/parsers/sorqry_parser' require 'syspro/business_objects/parsers/portor_parser' require 'syspro/business_objects/parsers/portoi_parser' require 'syspro/business_objects/parsers/comsfm_parser' +require 'syspro/business_objects/parsers/invqry_parser' # Main Module module Syspro diff --git a/lib/syspro/business_objects/invqry.rb b/lib/syspro/business_objects/invqry.rb new file mode 100644 index 0000000..00c5222 --- /dev/null +++ b/lib/syspro/business_objects/invqry.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'syspro/business_objects/parsers/invqry_parser' +require 'erb' + +module Syspro + module BusinessObjects + class InvQry < ApiResource + include Syspro::ApiOperations::Query + include Syspro::BusinessObjects::Parsers + + attr_accessor :key_stock_code, + :filter_warehouse_list, # seperated by commas + :option + + def call(user_id) + xml_in = template.result(binding) + business_object = 'INVQRY' + params = { 'UserId' => user_id, 'BusinessObject' => business_object, 'XmlIn' => xml_in } + resp = InvQry.query(params) + + parse_response(resp) + end + + def template + ERB.new File.read(File.expand_path('schemas/invqry.xml.erb', File.dirname(__FILE__))), nil, '%' + end + + def parse_response(resp) + handle_errors(resp) + parser = InvQryParser.new(resp[0].data) + parser.parse + end + + def render_xml(inner_text, dflt_value = "") + inner_text ? inner_text.to_s : dflt_value + end + end + end +end diff --git a/lib/syspro/business_objects/models/inv.rb b/lib/syspro/business_objects/models/inv.rb new file mode 100644 index 0000000..b66d33e --- /dev/null +++ b/lib/syspro/business_objects/models/inv.rb @@ -0,0 +1,83 @@ +module Syspro + module BusinessObjects + module Models + class Inv + attr_accessor :warehouse_totals, + :warehouse_items, + :stock_item, + :system_information + + def initialize + @warehouse_totals = WarehouseTotals.new + @warehouse_items = [] + @stock_item = StockItem.new + @system_information = SystemInformation.new + end + + def addWarehouseItem(new_hash) + w = WarehouseItem.new + + # copy hash items that match into new warehouse item + new_hash.keys.each do |k| + w.send("#{k.to_s}=", new_hash[k]) if w.methods.include? k + end + + @warehouse_items.push(w) + end + end + + class WarehouseTotals + attr_accessor :qty_on_hand, + :available_qty + + # Not all xml parsed, see https://infozone.syspro.com/Support/businessobjectslibrary/INVQRYOUT.XML + end + + class WarehouseItem + attr_accessor :warehouse, + :description, + :qty_on_hand, + :available_qty, + :qty_on_order, + :qty_in_inspection, + :minimum_qty, + :maximum_qty, + :qty_on_back_order, + :qty_allocated, + :mtd_qty_received, + :mtd_qty_adjusted, + :mtd_qty_issued, + :ytd_qty_sold, + :prev_year_qty_sold, + :qty_in_transit, + :qty_allocated_wip, + :wip_qty_reserved, + :mtd_qty_sold, + :mtd_qty_trf, + :user_field1, + :user_field2, + :user_field3, + :default_bin, + :unit_cost, + :future_free, + :quantity_dispatch_not_invoiced + end + + class StockItem + attr_accessor :stock_code, + :description, + :long_desc + # Not all xml parsed, see https://infozone.syspro.com/Support/businessobjectslibrary/INVQRYOUT.XML + end + + class SystemInformation + attr_accessor :css_style, + :language, + :company_id, + :company_name + # Not all xml parsed, see https://infozone.syspro.com/Support/businessobjectslibrary/INVQRYOUT.XML + end + end + end +end + diff --git a/lib/syspro/business_objects/models/inv_qry_options.rb b/lib/syspro/business_objects/models/inv_qry_options.rb new file mode 100644 index 0000000..3196029 --- /dev/null +++ b/lib/syspro/business_objects/models/inv_qry_options.rb @@ -0,0 +1,35 @@ +module Syspro + module BusinessObjects + module Models + class InvQryOptions + attr_accessor :multi_media_image_type, + :include_history, + :include_bins, + :include_lots, + :include_serials, + :include_movements, + :movement_date_sequence, + :movement_start_date, + :max_number_movements, + :include_custom_forms, + :include_movement_issues, + :include_movement_transfers, + :include_movement_receipts, + :include_movement_physical, + :include_movement_adjustments, + :include_movement_cost_changes, + :include_movement_cost_mods, + :include_movement_invoices, + :include_movement_credit_notes, + :include_movement_debit_notes, + :include_movement_dispatch_notes, + :include_ecc, + :xsl_stylesheet + + end + end + end +end + + + diff --git a/lib/syspro/business_objects/parsers/invqry_parser.rb b/lib/syspro/business_objects/parsers/invqry_parser.rb new file mode 100644 index 0000000..a47ea8b --- /dev/null +++ b/lib/syspro/business_objects/parsers/invqry_parser.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module Syspro + module BusinessObjects + module Parsers + class InvQryParser + attr_reader :doc + + def initialize(doc) + @calculated_weight = 0 + @doc = doc + end + + def parse + parsed_inv = Syspro::BusinessObjects::Models::Inv.new + + doc.xpath("//WarehouseItem").each do |wh| + parsed_inv.addWarehouseItem({ + "warehouse": wh.xpath("Warehouse").text, + "description": wh.xpath("Description").text, + "qty_on_hand": wh.xpath("QtyOnHand").text, + "available_qty": wh.xpath("AvailableQty").text, + "qty_on_order": wh.xpath("QtyOnOrder").text, + "qty_in_inspection": wh.xpath("QtyInInspection").text, + "minimum_qty": wh.xpath("MinimumQty").text, + "maximum_qty": wh.xpath("MaximumQty").text, + "qty_on_back_order": wh.xpath("QtyOnBackOrder").text, + "qty_allocated": wh.xpath("QtyAllocated").text, + "mtd_qty_received": wh.xpath("MtdQtyReceived").text, + "mtd_qty_adjusted": wh.xpath("MtdQtyAdjusted").text, + "mtd_qty_issued": wh.xpath("MtdQtyIssued").text, + "ytd_qty_sold": wh.xpath("YtdQtySold").text, + "prev_year_qty_sold": wh.xpath("PrevYearQtySold").text, + "qty_in_transit": wh.xpath("QtyInTransit").text, + "qty_allocated_wip": wh.xpath("QtyAllocatedWip").text, + "wip_qty_reserved": wh.xpath("WipQtyReserved").text, + "mtd_qty_sold": wh.xpath("MtdQtySold").text, + "mtd_qty_trf": wh.xpath("MtdQtyTrf").text, + "user_field1": wh.xpath("UserField1").text, + "user_field2": wh.xpath("UserField2").text, + "user_field3": wh.xpath("UserField3").text, + "default_bin": wh.xpath("DefaultBin").text, + "unit_cost": wh.xpath("UnitCost").text, + "future_free": wh.xpath("FutureFree").text, + "quantity_dispatch_not_invoiced": wh.xpath("QuantityDispatchNotInvoiced").text + }) + end + + si = doc.xpath("InvQuery/SystemInformation").first + if si + parsed_inv.system_information.css_style = si.xpath("CssStyle").text + parsed_inv.system_information.language = si.xpath("Language").text + parsed_inv.system_information.company_id = si.xpath("CompanyId").text + parsed_inv.system_information.company_name = si.xpath("CompanyName").text + end + + st = doc.xpath("InvQuery/StockItem").first + if st + parsed_inv.stock_item.stock_code = st.xpath("StockCode").text + parsed_inv.stock_item.description = st.xpath("Description").text + parsed_inv.stock_item.long_desc = st.xpath("LongDesc").text + end + + wt = doc.xpath("InvQuery/WarehouseTotals").first + if wt + parsed_inv.warehouse_totals.qty_on_hand = wt.xpath("QtyOnHand").text + parsed_inv.warehouse_totals.available_qty = wt.xpath("AvailableQty").text + end + + parsed_inv + end + end + end + end +end \ No newline at end of file diff --git a/lib/syspro/business_objects/schemas/comsfm.xml.erb b/lib/syspro/business_objects/schemas/comsfm.xml.erb index 8f21bc5..a59a60c 100644 --- a/lib/syspro/business_objects/schemas/comsfm.xml.erb +++ b/lib/syspro/business_objects/schemas/comsfm.xml.erb @@ -1,5 +1,5 @@ - <%= @validate_only ? "Y" : "N" %> + <%= @validate_only ? @validate_only : "N" %> \ No newline at end of file diff --git a/lib/syspro/business_objects/schemas/invqry.xml.erb b/lib/syspro/business_objects/schemas/invqry.xml.erb new file mode 100644 index 0000000..b91212b --- /dev/null +++ b/lib/syspro/business_objects/schemas/invqry.xml.erb @@ -0,0 +1,38 @@ + + + <% if @key_stock_code %> + + <%= @key_stock_code%> + + <% end %> + <% if @option %> + + <% end %> + + + + \ No newline at end of file diff --git a/test/cassettes/test_int_query.yml b/test/cassettes/test_int_query.yml new file mode 100644 index 0000000..a126c67 --- /dev/null +++ b/test/cassettes/test_int_query.yml @@ -0,0 +1,180 @@ +--- +http_interactions: +- request: + method: get + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/logon?CompanyId=&CompanyPassword=&Operator=&OperatorPassword= + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Syspro/7 RubyBindings/1.0.0.alpha.2 + Content-Type: + - application/x-www-form-urlencoded + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/octet-stream + Server: + - Microsoft-HTTPAPI/2.0 + Date: + - Wed, 16 Jan 2019 12:31:24 GMT + Content-ength: + - '36' + body: + encoding: UTF-8 + string: '84301CEF7CD018459F8CF02873A278A000 ' + http_version: + recorded_at: Wed, 16 Jan 2019 12:31:23 GMT +- request: + method: get + uri: http://syspro.wildlandlabs.com:90/SYSPROWCFService/Rest/Query/Query?BusinessObject=INVQRY&UserId=84301CEF7CD018459F8CF02873A278A000%20%20&XmlIn=%3C?xml%20version=%221.0%22%20encoding=%22Windows-1252%22?%3E%0A%3CQuery%20xmlns:xsd=%22http://www.w3.org/2000/10/XMSchema-instance%22%20xsd:noNamespaceSchemaocation=%22INVQRY.XSD%22%3E%0A%20%20%0A%20%20%3CKey%3E%0A%20%20%20%20%3CStockCode%3E1003%3C/StockCode%3E%0A%20%20%3C/Key%3E%0A%20%20%0A%20%20%0A%20%20%3COption%3E%0A%20%20%20%20%3CMultiMediaImageType%3EGIF%3C/MultiMediaImageType%3E%0A%20%20%20%20%3CIncludeHistory%3EN%3C/IncludeHistory%3E%0A%20%20%20%20%3CIncludeBins%3EN%3C/IncludeBins%3E%0A%20%20%20%20%3CIncludeots%3EY%3C/Includeots%3E%0A%20%20%20%20%3CIncludeSerials%3EN%3C/IncludeSerials%3E%0A%20%20%20%20%3CIncludeMovements%3EN%3C/IncludeMovements%3E%0A%20%20%20%20%3CMovementDateSequence%3EA%3C/MovementDateSequence%3E%0A%20%20%20%20%3CMovementStartDate%3E2001-01-12%3C/MovementStartDate%3E%0A%20%20%20%20%3CMaxNumberMovements%3E100%3C/MaxNumberMovements%3E%0A%20%20%20%20%3CIncludeCustomForms%3EN%3C/IncludeCustomForms%3E%0A%20%20%20%20%3CIncludeMovementIssues%3EN%3C/IncludeMovementIssues%3E%0A%20%20%20%20%3CIncludeMovementTransfers%3EN%3C/IncludeMovementTransfers%3E%0A%20%20%20%20%3CIncludeMovementReceipts%3EN%3C/IncludeMovementReceipts%3E%0A%20%20%20%20%3CIncludeMovementPhysical%3EN%3C/IncludeMovementPhysical%3E%0A%20%20%20%20%3CIncludeMovementAdjustments%3EN%3C/IncludeMovementAdjustments%3E%0A%20%20%20%20%3CIncludeMovementCostChanges%3EN%3C/IncludeMovementCostChanges%3E%0A%20%20%20%20%3CIncludeMovementCostMods%3EN%3C/IncludeMovementCostMods%3E%0A%20%20%20%20%3CIncludeMovementInvoices%3EN%3C/IncludeMovementInvoices%3E%0A%20%20%20%20%3CIncludeMovementCreditNotes%3EN%3C/IncludeMovementCreditNotes%3E%0A%20%20%20%20%3CIncludeMovementDebitNotes%3EN%3C/IncludeMovementDebitNotes%3E%0A%20%20%20%20%3CIncludeMovementDispatchNotes%3EN%3C/IncludeMovementDispatchNotes%3E%0A%20%20%20%20%3CIncludeEcc%3EN%3C/IncludeEcc%3E%0A%20%20%20%20%3CXslStylesheet%3E%3C/XslStylesheet%3E%0A%20%20%3C/Option%3E%0A%20%20%0A%20%20%3CFilter%3E%0A%20%20%20%20%3CWarehouse%20FilterType=%22%22%20FilterValue=%22P0%22/%3E%0A%20%20%3C/Filter%3E%0A%3C/Query%3E + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Syspro/7 RubyBindings/1.0.0.alpha.2 + Content-Type: + - application/x-www-form-urlencoded + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/octet-stream + Server: + - Microsoft-HTTPAPI/2.0 + Date: + - Wed, 16 Jan 2019 12:31:25 GMT + Content-ength: + - '13919' + body: + encoding: UTF-8 + string: "\nanguage='05' + anguage2='EN' CssStyle='' DecFormat='1' DateFormat='01' Role='01' + Version='7.0.042' OperatorPrimaryRole=' '>\n\n1003\nMWN + MIX\n<ongDesc>MIDWEST NATIVE SPEARMINT MIXongDesc>\n + \ 3\n\n\n\n\nB\nKG\n + \ 2.204620\nM\nDR\n + \ 400.000000\nM\nB\n + \ 1.000000\nM\nN\nM\nMade-in\n\nSMWN\nMIDWEST + NATIVE SPEARMINT\n\n\n\n\nT\nTraceable + item\nN\nNon-MPS item\n\n\n\n00000\nYes\n<abourCost> + \ 0.00000abourCost>\nabourCost>0.000abourCost>\n + \ 0.00000\n0.000\n + \ 0.00000\n0.000\n + \ 0.00000\n0.000\n + \ 0.00000\n0.000\n + \ 0.00000\n0.000\n$\n + \ 400.000000\nI\nIncluded\n\n + \ 400.000000\n400.000\n + \ 0\neadTime> 0eadTime>\n\n\n + \ 0.00000\n\n\n\n<eadTime> + \ 0eadTime>\nH1\nHARRAH-ABBEEMINT + OWNED\n 0.00\n0.00\nA\nNO + TAX\nN\n + \ 0\nifeInDays> 0ifeInDays>\nN\n\n\n\n\n\n\nA\not + for lot\n 1\nEBQ + Quantity\n 0.000000\n0.000\n100\n<owevelCode> + 0owevelCode>\n\n2006-03-21\n + \ 0.00000\n0.000\n + \ 0.00000\n0.000\n + \ 0.00000\n0.000\n + \ 0.00000\n0.000\n + \ 0.00000\n0.000\n + 0.0000\n0.00\n + \ 1.000000\n 0.000000\nA\nC\nCoded\n + 0\nA\nNO TAX\n\n\n<istPriceCode>AistPriceCode>\nN\nNone\nN\nNot + applicable\nN\nNone\n\nY\nN\nY\nN\nB\n + \ 0.00000\n\n\n0.000\n$\n + \ 0.000\n\n\n\n\n\n\n\n\n\n\nP0\nPORTAGE-GROWER + OWNED\n 747.800000\n747.800\n + \ 747.800000\n747.800\n + \ 806.730000\n806.730\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\nastSale/>\nastCostChange/>\nastCostEntered/>\n\n\n\nP0\n + \ 0.00000\n 1554.530000\n1,554.530\n + \ 0.000000\n0.000\n\nevel> + \ 0.000000evel>\nevel>0.000evel>\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\nocation>P0ocation>\n\n\n<andedCostMultiplier> + \ 1.000000andedCostMultiplier>\nandedCostMultiplier>1.000andedCostMultiplier>\n\n\nFIFO\n<astCostEntered> + \ 0.00000astCostEntered>\n 0.00000\n<astNonMerchandise> + \ 0.00000astNonMerchandise>\n\n\n + \ 0.00\n\n\n + \ 0.00\n 0.00\n + \ 0.00\n 0.00\n\n<astActivityDates>\nastStockMovement>2017/08/02astStockMovement>\nastPurchase>2017/08/02astPurchase>\nastCount/>\n2006/03/21\nastActivityDates>\n<otItem>\n<otJob>064017otJob>\n\n\n064017\n\n\n + \ 283.200000\n283.200\n + \ 283.200000\n283.200\n\n<otHoldFlag/>\n\notItem>\n<otItem>\n<otJob>064020otJob>\n\n\n064020\n\n\n + \ 148.000000\n148.000\n + \ 148.000000\n148.000\n\n<otHoldFlag/>\n\notItem>\n<otItem>\n<otJob>064042otJob>\n\n\n064042\n\n\n + \ 25.600000\n25.600\n + \ 25.600000\n25.600\n\n<otHoldFlag/>\n\notItem>\n<otItem>\n<otJob>064064otJob>\n\n\n064064\n\n\n + \ 291.000000\n291.000\n + \ 291.000000\n291.000\n\n<otHoldFlag/>\n\notItem>\n\n\n + \ 747.800000\n747.800\n + \ 747.800000\n747.800\n + \ 806.730000\n806.730\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.00000\n 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\n\nevel> + \ 0.000000evel>\nevel>0.000evel>\n + \ 0.000000\n0.000\n + \ 0.000000\n0.000\nocation>P0ocation>\n\n\n<andedCostMultiplier> + \ 1.000000andedCostMultiplier>\nandedCostMultiplier>1.000andedCostMultiplier>\n\n\nFIFO\n<astCostEntered> + \ 0.00000astCostEntered>\n 0.00000\n<astNonMerchandise> + \ 0.00000astNonMerchandise>\n\n\n + \ 0.00\n\n\n + \ 0.00\n 0.00\n + \ 0.00\n 0.00\n\n\n\n " + http_version: + recorded_at: Wed, 16 Jan 2019 12:31:24 GMT +recorded_with: VCR 4.0.0 diff --git a/test/cassettes/test_logon.yml b/test/cassettes/test_logon.yml index e914747..f29d75f 100644 --- a/test/cassettes/test_logon.yml +++ b/test/cassettes/test_logon.yml @@ -25,12 +25,12 @@ http_interactions: Server: - Microsoft-HTTPAPI/2.0 Date: - - Wed, 09 Jan 2019 22:58:07 GMT + - Mon, 14 Jan 2019 22:56:14 GMT Content-ength: - - '36' + - '32' body: encoding: UTF-8 - string: '8513E1BCC242674D9F3CC6CD27658BFA00 ' + string: 'ERROR: Invalid operator password' http_version: - recorded_at: Wed, 09 Jan 2019 22:58:05 GMT + recorded_at: Mon, 14 Jan 2019 22:56:14 GMT recorded_with: VCR 4.0.0 diff --git a/test/invqry_test.rb b/test/invqry_test.rb new file mode 100644 index 0000000..800ad83 --- /dev/null +++ b/test/invqry_test.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'test_helper' + +class InvQryTest < Minitest::Test + extend Minitest::Spec::DSL + before { VCR.insert_cassette name } + after { VCR.eject_cassette } + + let(:username) { ENV['SYSPRO_USERNAME'] } + let(:password) { ENV['SYSPRO_PASSWORD'] } + let(:company) { ENV['SYSPRO_COMPANY'] } + let(:company_password) { '' } + + let(:user_id) do + Syspro::Logon.logon(username, password, company, company_password) + end + + def test_int_query + invqry_req = Syspro::BusinessObjects::InvQry.new + + invqry_req.key_stock_code = '1003' + invqry_req.filter_warehouse_list = 'P0' + invqry_req.option = Syspro::BusinessObjects::Models::InvQryOptions.new + invqry_req.option.include_lots = "Y" + + invqry_rsp = invqry_req.call(user_id.guid) + + assert_kind_of Syspro::BusinessObjects::Models::Inv, invqry_rsp + end +end \ No newline at end of file diff --git a/test/portoi_test.rb b/test/portoi_test.rb index 6daad24..3ddf648 100644 --- a/test/portoi_test.rb +++ b/test/portoi_test.rb @@ -69,7 +69,6 @@ class PorToiTest < Minitest::Test po.order_details = Syspro::BusinessObjects::Models::PurchaseOrders::OrderDetails.new po.order_details.stock_lines = [line1] - binding.pry syspro_po = po.call(user_id.guid) -- libgit2 0.21.4