require "httparty" module Comee module Core class BeoService def initialize(ids) @atlas_token = ENV.fetch("ATLAS_TOKEN") @atlas_base_url = ENV.fetch("ATLAS_BASE_URL") @encoded_auth = ENV.fetch("ENCODED_AUTH") @ids = ids end def preprocess_data(xml: false) @ids.each do |id| @line_items = if xml shipment_items = Comee::Core::ShipmentItem.includes(sales_order_item: :sales_order) .where(sales_order_item: {sales_order_id: id}) sales_order_item_ids = shipment_items.map { |item| item.sales_order_item_id } Comee::Core::SalesOrderItem.where(id: sales_order_item_ids) else Comee::Core::ShipmentInstructionItem.includes(shipment_item: {sales_order_item: :sales_order}) .where(shipment_instruction_id: id) end sales_order_ids = if xml @line_items.map(&:sales_order_id) else @line_items.map { |item| item.shipment_item.sales_order_item.sales_order.id } end @customs_details = Comee::Core::CustomsDetail.includes(sales_order: :customer_order).where(sales_order_id: sales_order_ids) @transportation_routes = [] @position_data = [] packaging_types = @line_items.map do |line_item| if xml Comee::Core::ShipmentItem.find_by(sales_order_item_id: line_item.id).package_type.split(" ")[0] else line_item.shipment_item.package_type.split(" ")[0] end end.uniq @no_of_packages = packaging_types.count @package_type = packaging_types.count == 1 && packaging_types.first == "PX" ? "PX" : "PK" @total_weight = @line_items.sum do |line_item| if xml xml_line = Comee::Core::ShipmentInstructionItem.where(shipment_item_id: Comee::Core::ShipmentItem .find_by(sales_order_item_id: line_item.id).id).first (xml_line.quantity * xml_line.weight).round(2) else (line_item.quantity * line_item.weight).round(2) end end raise(StandardError, "No customs detail filed for sales order") unless @customs_details.present? @address = Comee::Core::ClientAddress.find(@customs_details[0].sales_order.delivery_address.to_i) @customs_details[0].transportation_route.each_with_index do |tr, index| if xml @transportation_routes << tr["route"][0, 2] else @transportation_routes << tr["route"][0, 2] unless index.zero? end end @data = xml ? generate_xml : prepare_data end xml ? @data : JSON(@data) end def position_data position_items = [] @line_items.each_with_index do |line_item, index| si = line_item.shipment_item customs_detail = @customs_details.find_by(sales_order_id: si.sales_order_item.sales_order_id) mp = Comee::Core::MasterPrice.includes(:country_of_origin).find_by(primary: true, product_id: si .sales_order_item.product.id) position_items << { "warePositionsnummer": index + 1, "wareWarennummerKN8": si.sales_order_item.product.hs_code, "wareWarenbezeichnung": si.sales_order_item.product.customs_description, "wareRegistriernummerFremdsystem": customs_detail.sales_order.customer_order.order_number, "wareUrsprungsbundesland": mp.state_of_origin.split(" ")[0], "wareEigenmasse": line_item.weight * line_item.quantity, "wareRohmasse": index.zero? ? @total_weight : 0, "ausfuhrLand": "DE", "ursprungsland": mp.country_of_origin.code, "beantragtesVerfahren": "10", "vorhergehendesVerfahren": "00", "zusatzlichesVerfahren": "F61", "aussenhandelsstatistikMenge": si.sales_order_item.quantity, "aussenhandelsstatistikWert": (si.sales_order_item.quantity * si.sales_order_item.price * 1.05).round(2).to_i, "packstuck": [ { "packstuckNummer": index + 1, "packstuckAnzahl": index.zero? ? @no_of_packages : 0, "packstuckVerpackungsart": @package_type, "packstuckZeichenNummern": customs_detail.sales_order.customer_order.order_number } ] } end position_items end def prepare_data { "dataIdentifier": "", "kundenNumber": "", "mandantId": "", "module": { "ausfuhr": { "data": "true", "email": "", "autosend": "false" } }, "messageData": { "ausfuhr": { "expdat": [ { "kopf": { "eoriNiederlassungsnummer": "DE47897410000", "artderAnmeldung": @customs_details[0].registration_type.split(" ")[0], "artderAnmeldungAusfuhr": @customs_details[0].export_declaration_type.split(" ")[0], "ausfuhrLand": "DE", "beteiligtenKonstellation": @customs_details[0].participant_constellation.split(" ")[0], "sicherheit": @customs_details[0].registration_type.split(" ")[0] == "CO" ? 0 : 2, "container": @customs_details[0].containerized == true ? 1 : 0, "bestimmungsLand": @customs_details[0].destination_country.split(" ")[0], "referenznummerUCR": @customs_details.map(&:sales_order).map(&:customer_order).map(&:consignee).join(" "), "lrn": @customs_details.map(&:sales_order).map(&:customer_order).map(&:order_number).join(" "), "beforderungsmittelImInlandVerkehrszweig": @customs_details[0].mode_of_transport.split(" ")[0], "beforderungsmittelderGrenzeVerkehrszweig": @customs_details[0].mode_of_transport_at_border.split(" ")[0], "beforderungsmittelderGrenzeArt": @customs_details[0].mode_of_transport_type.split(" ")[0], "beforderungsmittelderGrenzeKennzeichen": "UNBEKANNT", "beforderungsmittelderGrenzeStaatszugehorigkeit": @customs_details[0].mode_of_transport_nationality.split(" ")[0], "gesamtRohmasse": @total_weight, "beforderungsmittelBeimAbgang": [ { "sequenznummer": "1", "artderIdentifikation": @customs_details[0].type_of_identification.split(" ")[0], "kennzeichen": "UNBEKANNT", "staatszugehorigkeit": @customs_details[0].nationality.split(" ")[0] } ], "ausfuhrzollstelleDienststellennummer": @customs_details[0].export_customs_office.split(" ")[0], "vorgeseheneAusgangszollstelleDienststellennummer": @customs_details[0].customs_office_of_exit.split(" ")[0], "geschaftsvorgangArt": "11", "geschaftsvorgangRechnungspreis": @customs_details[0].sales_order.total_price.round(2), "geschaftsvorgangWahrung": "EUR", "beforderungsroute": { "ausgewahlteLander": @transportation_routes, "beforderungsVon": "DE", "beforderungsBis": @customs_details[0].destination_country.split(" ")[0] }, "empfanger": { "tin": "", "niederlassungsNummer": "", "name": @customs_details[0].sales_order.customer_order.client.name, "strasse": @address.street, "plz": @address.postal_code, "ort": @address.city, "land": @address.country.code }, "warenortArtdesOrtes": "B", "warenortArtDerOrtsbestimmung": "Y", "warenortZusatzlicheKennung": @customs_details[0].additional_identifier.split(" ")[0], "lieferbedingungIncotermCode": @customs_details[0].delivery_term_code[0, 3], "lieferbedingungOrt": "Hamburg" }, "position": position_data } ] } } } end def send_customs_details response = HTTParty.post("#{@atlas_base_url}/PutData", headers: {'Content-Type': "application/json", 'token': @atlas_token, 'bearertoken': bearer_token}, body: preprocess_data) unless [200, 201].include?(response.code) raise(StandardError, "Failed to send POST request with token: #{response.code} - #{response.body}") end JSON.parse(response.body) end def xml_position_data(xml) @line_items.each_with_index do |line_item, index| customs_detail = @customs_details.find_by(sales_order_id: line_item.sales_order_id) mp = Comee::Core::MasterPrice.includes(:country_of_origin).find_by(primary: true, product_id: line_item.product.id) xml.Position do xml.Positionsnummer (index + 1).to_s xml.Warenbezeichnung line_item.product.customs_description xml.Registriernummer_Fremdsystem customs_detail.sales_order.customer_order.order_number xml.Kennnummer_der_Sendung @customs_details.map(&:sales_order).map(&:customer_order).map(&:consignee).join(" ") xml.Ursprungsbundesland mp.state_of_origin.split(" ")[0] xml.Ursprungsland mp.country_of_origin.code xml.Eigenmasse line_item.product.weight * line_item.quantity xml.Rohmasse index.zero? ? @total_weight : 0 xml.Gefahrgutnummer_UNDG xml.Warennummer_KN8 line_item.product.hs_code xml.Verfahren do xml.angemeldetes "10" xml.vorangegangenes "00" xml.weiteres "F61" xml.Ausfuhrerstattung end xml.Aussenhandelsstatistik do xml.Menge line_item.quantity xml.Wert (line_item.quantity * line_item.price * 1.05).round(2).to_i end xml.Vorpapier do xml.Typ xml.Referenz xml.Zusatz end xml.Packstuecke do xml.Anzahl index.zero? ? @no_of_packages : 0 xml.Nummer index + 1 xml.Verpackungsart @package_type xml.Zeichen_Nummern customs_detail.sales_order.customer_order.order_number end xml.Unterlage do xml.Qualifizierung xml.Typ xml.Referenz xml.Zusatz xml.Detail xml.Datum_der_Ausstellung xml.Datum_des_Gueltigkeitsendes xml.Wert xml.Masseinheit xml.Aschreibungsmenge end end end end def generate_xml Nokogiri::XML::Builder.new(encoding: "Windows-1252") do |xml| xml.BEO_ATLAS("xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema") do xml.Nachricht do xml.Zollnummer_Sender "4789741" xml.Nachrichten_Typ "DEXPDF" xml.BEO_Atlas_KundenID xml.Anwendung xml.Name "MAVEKO" end xml.Kopf do xml.EORI_Number "DE47897410000" xml.Art_der_Ausfuhranmeldung @customs_details[0].export_declaration_type.split(" ")[0] xml.Ausfuhrland "DE" xml.Bestimmungsland @customs_details[0].destination_country.split(" ")[0] xml.Container @customs_details[0].containerized == true ? 1 : 0 xml.send("Beteiligten-Konstellation", @customs_details[0].participant_constellation.split(" ")[0]) xml.send("Gesamt-Rohmasse", @total_weight) xml.Kennnummer_der_Sendung @customs_details.map(&:sales_order).map(&:customer_order).map(&:consignee).join(" ") xml.Bezugsnummer @customs_details.map(&:sales_order).map(&:customer_order).map(&:order_number).join(" ") xml.inlandischerVerkehrszweig @customs_details[0].mode_of_transport.split(" ")[0] xml.verkehrszweigAnDerGrenze @customs_details[0].mode_of_transport_at_border.split(" ")[0] xml.Befoerderungsmittel_an_der_Grenze do xml.Art @customs_details[0].mode_of_transport_type.split(" ")[0] xml.Kennzeichen "UNBEKANNT" end xml.Befoerderungsmittel_am_Abgang do xml.sequenznummer "1" xml.Art @customs_details[0].type_of_identification.split(" ")[0] xml.Kennzeichen "UNBEKANNT" xml.Staatszugehoerigkeit @customs_details[0].nationality.split(" ")[0] end xml.Ausfuhrzollstelle do xml.Dienststellennummer @customs_details[0].export_customs_office.split(" ")[0] end xml.Vorgesehene_Ausgangszollstelle do xml.Dienststellennummer @customs_details[0].customs_office_of_exit.split(" ")[0] end xml.Geschaeftsvorgang do xml.Art "11" xml.Rechnungspreis @customs_details[0].sales_order.total_price.round(2) xml.Waehrung "EUR" end xml.Befoerderungsroute do @transportation_routes.each do |tr| xml.Land tr end end xml.Empfaenger do xml.Identifikationsart xml.TIN xml.Niederlassungsnummer xml.Name @customs_details[0].sales_order.customer_order.client.name xml.Strasse @address.street xml.PLZ @address.postal_code xml.Ort @address.city xml.Land @address.country.code end xml.Lieferbedingung do xml.send("Incoterm-Code", @customs_details[0].delivery_term_code[0, 3]) xml.Ort "Hamburg" xml.Land "DE" end end xml.Waren do xml_position_data(xml) end end end.to_xml end private def bearer_token response = HTTParty.post("#{@atlas_base_url}/authenticate", headers: { 'Authorization': "Basic #{@encoded_auth}", 'Accept': "application/json" }) raise(StandardError, "Failed to obtain bearer token: #{response.code} - #{response.body}") unless response.code == 200 JSON.parse(response.body)["accessToken"] end end end end