//--------------------------------------------------------------------------------------------- /// <summary> /// private Constructor for internal use only /// </summary> private PollingService() { FDatabase = VAT100Database.Instance; pollingTimer = new System.Timers.Timer(); StopPolling(); pollingTimer.Interval = DEFAULT_POLLING_INTERVAL; pollingTimer.Elapsed += OnTimerElapsedEvent; FPendingDocument = SelectNextDocument(); if (FPendingDocument != null) { PrepareHMRCRequest(FPendingDocument); } else { // Log.Add("Polling Service ready, but no VAT submissions are pending"); // Switch back to the default polling interval pollingTimer.Interval = DEFAULT_POLLING_INTERVAL; } // Documents could arrive at any time, so poll continuously StartPolling(); }
/// <summary> /// Sets the correct submission URL and polling interval for the /// supplied submission. Once this has been called, the PollingService /// will wait for the specified interval, and then call the HMRC /// service. /// </summary> /// <param name="record"></param> /// <returns></returns> private void PrepareHMRCRequest(DocumentRecord record) { pollingTimer.Interval = record.theDocument.pollingInterval * SECONDS_TO_TIMER_INTERVAL_MULTIPLIER; // Log.Add(string.Format("Preparing request for HMRC service, polling interval of {0}", pollingTimer.Interval)); switch (record.theDocument.documentType) { case "STD": FPendingDocument.docClass = HMRCFilingServiceProcessor.MESSAGE_CLASS_VAT_RETURN; destURL = HMRCFilingServiceProcessor.HMRC_LIVE_URL; break; case "TIL": FPendingDocument.docClass = HMRCFilingServiceProcessor.MESSAGE_CLASS_VAT_RETURN_TIL; destURL = HMRCFilingServiceProcessor.HMRC_LIVE_URL; break; case "DEV": FPendingDocument.docClass = HMRCFilingServiceProcessor.MESSAGE_CLASS_VAT_RETURN; destURL = HMRCFilingServiceProcessor.HMRC_DEV_URL; break; } return; }
/// <summary> /// Calls the HMRC Gateway web-service to retrieve the current status of the /// VAT Submission supplied in the Document Record, and returns an XML string /// of the results. /// </summary> /// <param name="record"></param> /// <returns></returns> private string RetrieveHMRCResponse(DocumentRecord record) { VAT100_Poll pollMessage = new VAT100_Poll(); pollMessage.Header.MessageDetails.Class = record.docClass; pollMessage.Header.MessageDetails.CorrelationID = record.theDocument.correlationID; string pollMsg; // Serialize the message to a string XmlSerializer xmlSerializer = new XmlSerializer(pollMessage.GetType()); //SS:04/06/2018:2018-R1:ABSEXCH-20538:VAT 100 Submissions failing due to HMRC gateway changes using (Utf8StringWriter textWriter = new Utf8StringWriter()) { xmlSerializer.Serialize(textWriter, pollMessage); pollMsg = textWriter.ToString(); } // Submit the resulting XML to HMRC // Create a request var request = (HttpWebRequest)WebRequest.Create(FPendingDocument.theDocument.PollingURL); var data = Encoding.ASCII.GetBytes(pollMsg); request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = data.Length; // Write the data to the request parameters using (var stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } // Send the request and get the response. var response = (HttpWebResponse)request.GetResponse(); // Get the response in a stream. var responseData = new StreamReader(response.GetResponseStream()).ReadToEnd(); // Convert the stream to a string string responseXML = responseData.ToString(); return(responseXML); }
/// <summary> /// Processes the currently selected document, if any, then selects /// the next document. This is the central polling routine, and is /// called from the timer event at regular intervals, so that all /// pending documents are cycled through one at a time. /// </summary> private int ProcessPendingDocument() { // Need to send a poll message to HMRC for the current pending submission // The message has all its fixed fields populated automatically. // We just have to set: // 1) Class // 2) CorrelationID int Result = 0; // Stop polling while we send a polling message so that we can't possibly re-enter (especially while debugging) // Poll timimg is not critical and the user won't see it happening. StopPolling(); try { // Ensure that we have a document to work with if (FPendingDocument != null) { // Get the response message and deal with it string responseXML = RetrieveHMRCResponse(FPendingDocument); Result = HandleHMRCResponse(responseXML); if (Result != 0) { Log.Add("Error handling HMRC response : Code " + Result.ToString()); } } FPendingDocument = SelectNextDocument(); if (FPendingDocument != null) { PrepareHMRCRequest(FPendingDocument); } } finally { StartPolling(); } return(Result); }
//--------------------------------------------------------------------------------------------- /// <summary> /// Scans the VAT100 tables in all companies to find the next VAT /// submission that is waiting for a response from HMRC, and returns /// a DocumentRecord containing the details. /// </summary> /// <returns></returns> private DocumentRecord SelectNextDocument() { Enterprise04.ICompanyDetail CompanyDetail; DocumentRecord document = null; // Cycle through all the companies int CompanyCount = FDatabase.tToolkit.Company.cmCount; for (int i = 1; i <= CompanyCount; i++) { // Point the Toolkit at the company path (getPendingVAT100Entry will // open and close the Toolkit using this path) CompanyDetail = FDatabase.tToolkit.Company.get_cmCompany(i); // Find any pending VAT Submission for this company and add it to the list VAT100Record Entry = FDatabase.GetPendingVAT100Entry(CompanyDetail.coCode); if (Entry != null) { document = new DocumentRecord(); document.theDocument = Entry; document.companyCode = CompanyDetail.coCode; document.companyPath = CompanyDetail.coPath; document.status = ServiceStatus.ssPolling; break; } } if (document == null) { // No more pending documents, so revert back to the // slow polling interval. pollingTimer.Interval = DEFAULT_POLLING_INTERVAL; } return(document); }
/// <summary> /// Adds a new VAT Return Submission to the VAT100 database table following receipt of an Acknowledgement. /// </summary> /// <param name="companyCode"></param> /// <param name="documentType"></param> /// <param name="userName"></param> /// <param name="request"></param> /// <param name="acknowledgement"></param> /// <returns></returns> public bool Add(string companyCode, string documentType, string userName, VAT100_GovTalkMessage request, VAT100_Acknowledgement acknowledgement) { // Create a Document for use by the Polling functions. DocumentRecord newDocument = new DocumentRecord(); newDocument.theDocument = new VAT100Record(); newDocument.companyCode = companyCode; newDocument.companyPath = FDatabase.GetCompanyPath(companyCode); // Fill in the details newDocument.theDocument.correlationID = acknowledgement.Header.MessageDetails.CorrelationID; newDocument.theDocument.IRMark = request.Body.IRenvelope.IRheader.IRMark.Value; DateTime timeNow = DateTime.Now; newDocument.theDocument.dateSubmitted = timeNow.ToString("ddMMyyyy HHmmss"); newDocument.theDocument.documentType = documentType; newDocument.theDocument.VATPeriod = request.Body.IRenvelope.IRheader.PeriodID; newDocument.theDocument.username = userName; newDocument.theDocument.status = (short)SubmissionStatus.ssSubmitted; newDocument.theDocument.pollingInterval = acknowledgement.Header.MessageDetails.ResponseEndPoint.PollInterval; newDocument.theDocument.VATDueOnOutputs = request.Body.IRenvelope.VATDeclarationRequest.VATDueOnOutputs; newDocument.theDocument.VATDueOnECAcquisitions = request.Body.IRenvelope.VATDeclarationRequest.VATDueOnECAcquisitions; newDocument.theDocument.VATTotal = request.Body.IRenvelope.VATDeclarationRequest.TotalVAT; newDocument.theDocument.VATReclaimedOnInputs = request.Body.IRenvelope.VATDeclarationRequest.VATReclaimedOnInputs; newDocument.theDocument.VATNet = request.Body.IRenvelope.VATDeclarationRequest.NetVAT; newDocument.theDocument.netSalesAndOutputs = request.Body.IRenvelope.VATDeclarationRequest.NetSalesAndOutputs; newDocument.theDocument.netPurchasesAndInputs = request.Body.IRenvelope.VATDeclarationRequest.NetPurchasesAndInputs; newDocument.theDocument.netECSupplies = request.Body.IRenvelope.VATDeclarationRequest.NetECSupplies; newDocument.theDocument.netECAcquisitions = request.Body.IRenvelope.VATDeclarationRequest.NetECAcquisitions; // PKR. 16/09/2015. ABSEXCH-16865. Add a temporary narrative so that the Submission record isn't empty. newDocument.theDocument.hmrcNarrative = "Acknowledgement received from HMRC."; newDocument.theDocument.PollingURL = acknowledgement.Header.MessageDetails.ResponseEndPoint.EndPoint; // Save the pending document int Res = 0; // CJS 2016-06-06 - ABSEXCH-17494 - VAT submission returning HMRC message to wrong company. // If we are not polling, it probably means that we are searching for or processing // an existing document, in which case it is not safe to use the VAT100Database singleton, // because it is already being used and might be open in the wrong company. Instead use // a new instance. if (!pollingTimer.Enabled) { Res = VAT100Database.GetNewInstance().AddVAT100Entry(newDocument.theDocument, companyCode); } else { Res = VAT100Database.Instance.AddVAT100Entry(newDocument.theDocument, companyCode); } if (Res != 0) { Log.Add(string.Format("Failed to save pending document for {0}. Error code : {1}", companyCode, Res)); return(false); } else { return(true); } }