Snippets Collections
SELECT 
    CASE 
        WHEN nombre = 'FEDERICO DE LEON' 
        THEN 'Jefe de Transformacion y Personas'
        ELSE PUENOM
    END AS PUENOM,
    AVG(SUELDO) AS Promedio_Sueldo_Anual,
    AVG(PRODUCTIVIDAD) AS Promedio_Productividad,
    AVG([COMISIONES VARIABLES]) AS Promedio_Comisiones_Variables_Anual,
    AVG(GRATIFICACION) AS Promedio_Gratificacion
FROM (
    SELECT 
        RTRIM(primer_nombre) + ' ' + RTRIM(primer_apellido) AS nombre,
        PUENOM,
        [PREMIOS] AS PRODUCTIVIDAD,
        [COMISIONES VARIABLES] AS [COMISIONES VARIABLES],
        [SUELDO] AS SUELDO,
        [GRATIFICACION]
    FROM (
        SELECT 
            vista_completa.primer_nombre,
            vista_completa.primer_apellido,
            PUESTOS.PUENOM,
            vista_completa.DEPARTAMENTO_NOMBRE,
            VISTA_GENEXCEL.FECHA_INGRESO,
            vista_genexcel.mes_y_anio,
            vista_genexcel.importe,
            vista_completa.fecha_de_egreso,
            CASE 
                WHEN vista_genexcel.CONCEPTO IN (45, 40, 46) THEN 'COMISIONES VARIABLES'
                ELSE vista_genexcel.concepto_nombre
            END AS concepto_nombre
        FROM 
            sueldosprod.dbo.fundef FUNDEF,
            sueldosprod.dbo.puestos PUESTOS,
            sueldosprod.dbo.vista_genexcel VISTA_GENEXCEL
            INNER JOIN sueldosprod.dbo.vista_completa VISTA_COMPLETA
                ON vista_genexcel.funcionario = vista_completa.ci
        WHERE  
            ((( vista_genexcel.mes_y_anio >= {ts '2024-08-01 00:00:00'} ) and ( vista_genexcel.mes_y_anio < {ts '2024-09-01 00:00:00'} )) or ( vista_genexcel.mes_y_anio >= {ts '2024-01-01 00:00:00'} and VISTA_GENEXCEL.CONCEPTO=2000))
            AND fundef.deffuncod = vista_completa.funcionario_codigo
            AND puestos.empcod = fundef.empcod
            AND puestos.puecod = fundef.puecod
            AND vista_genexcel.CONCEPTO IN (1, 44, 20, 45, 40, 46, 2000)
    ) AS SourceTable
    PIVOT (
        SUM(importe)
        FOR concepto_nombre IN ([PREMIOS], [COMISIONES VARIABLES], [SUELDO],[GRATIFICACION])
    ) AS PivotTable
    WHERE fecha_de_egreso IS NULL 
) AS ResultTable
where PUENOM != 'SIN DEFINIR'
GROUP BY 
    CASE 
        WHEN nombre = 'FEDERICO DE LEON' 
        THEN 'Jefe de Transformacion y Personas'
        ELSE PUENOM
    END
create procedure generate_range(startvalue integer, endvalue integer)
    returns (seq integer)
as
begin
    seq = startvalue;
    suspend;

    while (seq < endvalue) do
    begin
        seq = seq + 1;
        suspend;
    end
end;

/* Ex: select seq from generate_range(1, 100); */
A interoperabilidade no sistema bancário em Moçambique é uma realidade, tendo a SIMO como ponto central de gestão do sistema integrado de transações. Os bancos passaram a receber um conjunto de dados (em ficheiros de texto) transacionados nos seus terminais como POSs (compras) e ATMs (levantamentos/pagamentos) por meios de cartões. Por forma a garantir o melhor controle e rentabilidade (pelo menos uma transação) dos cartões. Considere a estrutura do ficheiro de transações de clientes (Clientes Ativos - 1 transação dentro de 90 dias, Clientes inativos - Transações acima de 90 dias) recebido abaixo:
Estrutura da tabela a considerar:
| Data | Transacao_ID | Terminal_ID | Tipo_Terminal | Valor | Cliente | Idade | Tipo_Cartao | Provincia | Distrito |
|---|---|---|---|---|---|---|---|---|---|
1. Crie um modelo normalizado e os devidos relacionamentos de acordo com a tabela.
2. Com base nas tabelas normalizadas escreva uma query que indique todos os clientes inactivos da província de Gaza, com mais levantamentos.
3. Tendo em consideração que para o banco uma “boa” rentabilidade é ter transações acima de MZN 1000. Indique a província do cliente menos rentável e com mais transações.
4. Ainda sobre a “boa rentabilidade”, indique a província com maior número de clientes activos e menos rentáveis.
5. Continuando sobre a “boa rentabilidade”, indique o cliente ativo mais velho, que realizou mais pagamentos no dia da independência.
  
-- 1. Criação do modelo normalizado

CREATE TABLE Cliente (
    ClienteID INT PRIMARY KEY,
    Nome VARCHAR(100),
    Idade INT
);

CREATE TABLE Cartao (
    CartaoID INT PRIMARY KEY,
    ClienteID INT,
    Tipo_Cartao VARCHAR(50),
    FOREIGN KEY (ClienteID) REFERENCES Cliente(ClienteID)
);

CREATE TABLE Localizacao (
    LocalizacaoID INT PRIMARY KEY,
    Provincia VARCHAR(50),
    Distrito VARCHAR(50)
);

CREATE TABLE Terminal (
    TerminalID VARCHAR(50) PRIMARY KEY,
    Tipo_Terminal VARCHAR(20),
    LocalizacaoID INT,
    FOREIGN KEY (LocalizacaoID) REFERENCES Localizacao(LocalizacaoID)
);

CREATE TABLE Transacao (
    TransacaoID VARCHAR(50) PRIMARY KEY,
    Data DATE,
    Valor DECIMAL(10,2),
    CartaoID INT,
    TerminalID VARCHAR(50),
    FOREIGN KEY (CartaoID) REFERENCES Cartao(CartaoID),
    FOREIGN KEY (TerminalID) REFERENCES Terminal(TerminalID)
);

-- Inserção de dados de exemplo (opcional, para teste)
-- Inserção de dados de exemplo

-- Clientes
INSERT INTO Cliente (ClienteID, Nome, Idade) VALUES
(1, 'João Silva', 35),
(2, 'Maria Santos', 28),
(3, 'Pedro Nunes', 45),
(4, 'Ana Oliveira', 50),
(5, 'Edson Famanda', 29),
(6, 'Luísa Costa', 42),
(7, 'António Mendes', 55),
(8, 'Sofia Rodrigues', 30),
(9, 'Miguel Almeida', 38),
(10, 'Beatriz Sousa', 47);

-- Cartões
INSERT INTO Cartao (CartaoID, ClienteID, Tipo_Cartao) VALUES
(101, 1, 'Débito'),
(102, 2, 'Crédito'),
(103, 3, 'Débito'),
(104, 4, 'Crédito'),
(105, 5, 'Débito'),
(106, 6, 'Crédito'),
(107, 7, 'Débito'),
(108, 8, 'Crédito'),
(109, 9, 'Débito'),
(110, 10, 'Crédito');

-- Localizações
INSERT INTO Localizacao (LocalizacaoID, Provincia, Distrito) VALUES
(201, 'Gaza', 'Xai-Xai'),
(202, 'Maputo', 'Matola'),
(203, 'Sofala', 'Beira'),
(204, 'Nampula', 'Nampula'),
(205, 'Gaza', 'Chibuto'),
(206, 'Inhambane', 'Inhambane'),
(207, 'Tete', 'Tete'),
(208, 'Zambézia', 'Quelimane'),
(209, 'Cabo Delgado', 'Pemba'),
(210, 'Niassa', 'Lichinga');

-- Terminais
INSERT INTO Terminal (TerminalID, Tipo_Terminal, LocalizacaoID) VALUES
('T001', 'ATM', 201),
('T002', 'POS', 202),
('T003', 'ATM', 203),
('T004', 'POS', 204),
('T005', 'ATM', 205),
('T006', 'POS', 206),
('T007', 'ATM', 207),
('T008', 'POS', 208),
('T009', 'ATM', 209),
('T010', 'POS', 210);

-- Transações (50 transações)
INSERT INTO Transacao (TransacaoID, Data, Valor, CartaoID, TerminalID) VALUES
('TR001', '2024-06-25', 500.00, 101, 'T001'),
('TR002', '2024-06-25', 1200.00, 102, 'T002'),
('TR003', '2024-05-15', 800.00, 103, 'T003'),
('TR004', '2024-06-25', 1500.00, 104, 'T004'),
('TR005', '2024-03-01', 300.00, 105, 'T005'),
('TR006', '2024-06-25', 2000.00, 106, 'T006'),
('TR007', '2024-06-01', 100.00, 107, 'T007'),
('TR008', '2024-06-10', 950.00, 108, 'T008'),
('TR009', '2024-06-15', 1100.00, 109, 'T009'),
('TR010', '2024-06-20', 750.00, 110, 'T010'),
('TR011', '2024-06-25', 600.00, 101, 'T001'),
('TR012', '2024-05-30', 1800.00, 102, 'T002'),
('TR013', '2024-04-22', 400.00, 103, 'T003'),
('TR014', '2024-06-25', 2500.00, 104, 'T004'),
('TR015', '2024-02-15', 200.00, 105, 'T005'),
('TR016', '2024-06-25', 3000.00, 106, 'T006'),
('TR017', '2024-05-18', 150.00, 107, 'T007'),
('TR018', '2024-06-05', 1050.00, 108, 'T008'),
('TR019', '2024-06-12', 900.00, 109, 'T009'),
('TR020', '2024-06-19', 1250.00, 110, 'T010'),
('TR021', '2024-06-25', 700.00, 101, 'T001'),
('TR022', '2024-06-02', 1600.00, 102, 'T002'),
('TR023', '2024-05-10', 550.00, 103, 'T003'),
('TR024', '2024-06-25', 2200.00, 104, 'T004'),
('TR025', '2024-01-20', 350.00, 105, 'T005'),
('TR026', '2024-06-25', 2800.00, 106, 'T006'),
('TR027', '2024-04-30', 180.00, 107, 'T007'),
('TR028', '2024-06-08', 1150.00, 108, 'T008'),
('TR029', '2024-06-14', 980.00, 109, 'T009'),
('TR030', '2024-06-22', 1450.00, 110, 'T010'),
('TR031', '2024-06-25', 850.00, 101, 'T001'),
('TR032', '2024-05-28', 2100.00, 102, 'T002'),
('TR033', '2024-04-18', 480.00, 103, 'T003'),
('TR034', '2024-06-25', 3200.00, 104, 'T004'),
('TR035', '2024-02-10', 280.00, 105, 'T005'),
('TR036', '2024-06-25', 3500.00, 106, 'T006'),
('TR037', '2024-05-22', 220.00, 107, 'T007'),
('TR038', '2024-06-03', 1350.00, 108, 'T008'),
('TR039', '2024-06-11', 1020.00, 109, 'T009'),
('TR040', '2024-06-18', 1650.00, 110, 'T010'),
('TR041', '2024-06-25', 920.00, 101, 'T001'),
('TR042', '2024-06-01', 2400.00, 102, 'T002'),
('TR043', '2024-05-08', 630.00, 103, 'T003'),
('TR044', '2024-06-25', 2900.00, 104, 'T004'),
('TR045', '2024-01-15', 380.00, 105, 'T005'),
('TR046', '2024-06-25', 3800.00, 106, 'T006'),
('TR047', '2024-04-25', 250.00, 107, 'T007'),
('TR048', '2024-06-07', 1550.00, 108, 'T008'),
('TR049', '2024-06-13', 1080.00, 109, 'T009'),
('TR050', '2024-06-21', 1850.00, 110, 'T010');

-- 2. Query para clientes inativos da província de Gaza, com mais levantamentos
SELECT c.ClienteID, c.Nome, COUNT(*) as NumLevantamentos
FROM Cliente c
JOIN Cartao ca ON c.ClienteID = ca.ClienteID
JOIN Transacao t ON ca.CartaoID = t.CartaoID
JOIN Terminal te ON t.TerminalID = te.TerminalID
JOIN Localizacao l ON te.LocalizacaoID = l.LocalizacaoID
WHERE l.Provincia = 'Gaza'
  AND te.Tipo_Terminal = 'ATM'
  AND t.Data < DATE_SUB(CURDATE(), INTERVAL 90 DAY)
GROUP BY c.ClienteID, c.Nome
ORDER BY NumLevantamentos DESC;

-- 3. Província do cliente menos rentável e com mais transações
SELECT l.Provincia
FROM Cliente c
JOIN Cartao ca ON c.ClienteID = ca.ClienteID
JOIN Transacao t ON ca.CartaoID = t.CartaoID
JOIN Terminal te ON t.TerminalID = te.TerminalID
JOIN Localizacao l ON te.LocalizacaoID = l.LocalizacaoID
GROUP BY l.Provincia
ORDER BY SUM(CASE WHEN t.Valor > 1000 THEN 1 ELSE 0 END) ASC, COUNT(*) DESC
LIMIT 1;

-- 4. Província com maior número de clientes ativos e menos rentáveis
SELECT l.Provincia
FROM Cliente c
JOIN Cartao ca ON c.ClienteID = ca.ClienteID
JOIN Transacao t ON ca.CartaoID = t.CartaoID
JOIN Terminal te ON t.TerminalID = te.TerminalID
JOIN Localizacao l ON te.LocalizacaoID = l.LocalizacaoID
WHERE t.Data >= DATE_SUB(CURDATE(), INTERVAL 90 DAY)
GROUP BY l.Provincia
ORDER BY COUNT(DISTINCT c.ClienteID) DESC, SUM(CASE WHEN t.Valor <= 1000 THEN 1 ELSE 0 END) DESC
LIMIT 1;

-- 5. Cliente ativo mais velho, com mais pagamentos no dia da independência
SELECT c.ClienteID, c.Nome, c.Idade, COUNT(*) as NumPagamentos
FROM Cliente c
JOIN Cartao ca ON c.ClienteID = ca.ClienteID
JOIN Transacao t ON ca.CartaoID = t.CartaoID
JOIN Terminal te ON t.TerminalID = te.TerminalID
WHERE te.Tipo_Terminal = 'POS'
  AND DAY(t.Data) = 25 AND MONTH(t.Data) = 6
  AND t.Data >= DATE_SUB(CURDATE(), INTERVAL 90 DAY)
GROUP BY c.ClienteID, c.Nome, c.Idade
ORDER BY c.Idade DESC, NumPagamentos DESC
LIMIT 1;
Create Schema demo;

CREATE TABLE demo.emp_info (
    emp_id CHAR(4),
    emp_name VARCHAR(20),
    skills VARCHAR(40),
    exp INT
);

INSERT INTO demo.emp_info
VALUES
("A123", "Rohit Jain", "SQL|C|R|Python|Tableau", 7),
("A124", "Aaina Singh", "SQLC|R|Tableau", 4),
("A125", "Mark John", "C|Python|Java", 10),
("A126", "Sam Keith", "SQL|C", 2),
("A127", "Kenny Ford", "SQL|C|R|Python|Power BI", 5);

CREATE TABLE demo.emp_sal_desig (
    emp_id CHAR(4),
    desig VARCHAR(20),
    salary FLOAT
);

INSERT INTO demo.emp_sal_desig
VALUES
("A123", "L3", 4500),
("A126", "L1", 2500),
("A121", "L2", 3500),
("A122", "L5", 9500);
1. Find the product id and unit list price of products belonging to the 'Yoghurt' category.


SELECT 
	product_id, 
	unit_list_price
FROM 
	products
WHERE 
	product_category = 'yoghurt';

-----------
  
2. Find all the information (i.e., all columns) of the managers with null values in their first name.

SELECT 
   	*
FROM
	managers
WHERE
	first_name IS NULL;

-----------
  
3. Find the sales ID and sales date where quantity sold on that date is greater than or equal to 2100.

SELECT 
	sales_id, 
	sales_date, 
	quantity_sold
FROM
	sales
WHERE
	quantity_sold >= 2100;

-----------

4. Find the manager ID and the sum of quantity sold  for all products (except those with ID 7001001) for each of the managers in the sales table and sort the output by manager ID in descending order.

SELECT 
	sales_manager_id,
	SUM(quantity_sold) AS quant_sold
FROM
	sales
WHERE
	product_id <> 7001001
GROUP BY  
	sales_manager_id
ORDER BY
	sales_manager_id DESC;

-----------

5. Find the product ID, product name, and unit production cost of the products with maximum unit production cost below $1.10 and sort the output by production cost in ascending order (HINT: Use HAVING).

SELECT 
    product_id,
    product_name,
    MAX(unit_production_cost) AS unit_production_cost
FROM
    products
GROUP BY 1 , 2
HAVING 
	MAX(unit_production_cost) < 1.10
ORDER BY 
	unit_production_cost DESC;


SELECT 
    product_id,
    product_name,
	unit_production_cost
FROM
    products
WHERE 
	unit_production_cost < 1.10
ORDER BY 
	unit_production_cost DESC;

-----------

6. Find the product ID and sales date with the highest quantity sold from sales transacted after 30 Oct 2021 (exclusive) except for products with IDs 7001001 and 7001002.


SELECT 
    product_id, 
	sales_date, 
	quantity_sold
FROM
    sales
WHERE
    sales_date > '2021-10-30'
    AND 
	product_id NOT IN (7001001 , 7001002)
ORDER BY 
	quantity_sold desc
LIMIT 1;



SELECT 
    product_id, 
	sales_date, 
	quantity_sold
FROM
    sales
WHERE
    sales_date > '2021-10-30';
SELECT 
  ISNULL(A.[staffpin], '') AS [staffpin],
  ISNULL(A.[staffname], '') AS [staffname], 
  ISNULL(A.[sex], '') AS [sex], 
  CONVERT(date, A.[dateofbirth]) AS [dateofbirth], 
  DATEDIFF(year,A.[dateofbirth],GETDATE()) AS [age], 
  CONVERT(date, A.[joining_date]) AS [joining_date], 
  DATEDIFF(year,A.[joining_date],GETDATE()) AS [service_length], 
  Case when ISNULL(A.[status], '') = 'C' then 'CONFIRM' else 'NON CONFIRM' end AS [status], 
  ISNULL(A.[jobstatus], '') AS [jobstatus], 
  ISNULL(A.[jobbase], '') AS [jobbase], 
  ISNULL(A.[designation], '') AS [designation], 
  ISNULL(A.[functionaldesignation], '') AS [Role], 
  ISNULL(A.[program_name], '') AS [program_name], 
  ISNULL(A.[project_name], '') AS [project_name], 
  ISNULL(A.[branchname], '') AS [branchname], 
  ISNULL(A.[district_name], '') AS [district_name], 
  ISNULL(A.[thana_name], '') AS [thana_name], 
  ISNULL(A.[division_name], '') AS [division_name], 
  CASE WHEN ISNULL(A.[level], 0) = 99 THEN 0 ELSE ISNULL(A.[level], 0) END AS [Grade], 
  ISNULL(A.[email_address], '') AS [email_address], 
  ISNULL(A.[mobile], '') AS [mobile], 
  ISNULL(slb.slab, 0) AS [slab], 
  ISNULL(A.[BirthDistrict], '') AS [HomeDistrict], 
  ISNULL(A.[LastEducation], '') AS [LastEducation], 
  ISNULL(A.[Last_Pms], '') AS [PMS2022], 
  ISNULL(A.[Second_Last_Pms], '') AS [PMS2021], 
  ISNULL(A.[Third_Last_Pms], '') AS [PMS2020], 
  ISNULL(A.[Last_Promotion_Date], '') AS [Last_Promotion_Date], 
  ISNULL(A.[Action_Taken], '') AS [Action_Taken], 
  ISNULL(A.[LastTransferDate], '') AS [LastTransferDate],  
  FDate [LastSlab]
FROM [HRReportDB].[HRReportDB].[dbo].[tblERP_Data] A
LEFT JOIN (Select PIN,max(FDate) as FDate from [HRReportDB].[HRPMS].[dbo].[tblSlab] group by PIN) B ON A.[staffpin] = B.[PIN]
LEFT JOIN (
				SELECT RIGHT('00000000' + pin, 8) AS PIN, ISNULL(slab, 0) AS slab FROM OPENQUERY(payroll_tool, 'SELECT pin, slab FROM payroll_tools.staffs')
		  ) slb ON a.staffPIN = slb.PIN 
WHERE a.jobstatus = 'Active' AND HRProgramId = '11'
  AND a.HRProgramId NOT IN ('00', '08', '41', '33', '05', '78', '66', '65', '50', '31') 
  AND a.jobbase IN ('REGULAR', 'SERVICE', 'CONTRACT')
UNION
SELECT  
  RIGHT(REPLICATE('0', 8) + k.pin,8) AS [staffpin], 
  ISNULL(k.name, '') AS StaffName, 
  ISNULL(A.[sex], '') AS [sex], 
  CONVERT(date, A.[dateofbirth]) AS [dateofbirth], 
  DATEDIFF(
    year, 
    A.[dateofbirth], 
    GETDATE()
  ) AS [age], 
  CONVERT(date, A.[joining_date]) AS [joining_date], 
  DATEDIFF(year,A.[joining_date],GETDATE()) AS [service_length], 
  ISNULL(k.job_status, '') AS [status],
  'Active' AS [jobstatus],  
  ISNULL(k.employee_type, '') AS [jobbase], 
  ISNULL(k.designation, '') AS [designation], 
  ISNULL(k.designation, '') AS [Role], 
  ISNULL(k.program_name, '') AS [program_name], 
  ISNULL(k.project_name, '') AS [project_name], 
  ISNULL(k.branch_name, '') AS [branchname], 
  ISNULL(Branch.DistrictName, '') AS [district_name], 
  ISNULL(Branch.UpazilaName, '') AS [thana_name], 
  ISNULL(Branch.DivisionName, '') AS [division_name], 
  RIGHT(
    REPLICATE('0', 3) + CAST(
      ISNULL(k.grade, 0) AS VARCHAR
    ), 
    3
  ) AS [Grade], 
  ISNULL(A.[email_address], '') AS [email_address], 
  ISNULL(A.[mobile], '') AS [mobile], 
  RIGHT(
    REPLICATE('0', 3) + CAST(
      CASE WHEN ISNULL(k.slab, 0) IN (0, 99, 16) THEN 0 ELSE k.slab END AS VARCHAR
    ), 
    3
  ) AS [slab], 
  ISNULL(A.[BirthDistrict], '') AS [HomeDistrict], 
  ISNULL(A.[LastEducation], '') AS [LastEducation], 
  ISNULL(A.[Last_Pms], '') AS [PMS2022], 
  ISNULL(A.[Second_Last_Pms], '') AS [PMS2021], 
  ISNULL(A.[Third_Last_Pms], '') AS [PMS2020], 
  ISNULL(A.[Last_Promotion_Date], '') AS [Last_Promotion_Date], 
  ISNULL(A.[Action_Taken], '') AS [Action_Taken], 
  ISNULL(A.[LastTransferDate], '') AS [LastTransferDate], 
  FDate [LastSlab] 
FROM 
  OPENQUERY(
    PAYROLL_TOOL, 'SELECT DISTINCT month, pin, name, designation, grade, slab, employee_type, job_status, project_name, project_code, branch_name, branch_code, program_name, program_code, salary_to_be_paid 
  FROM (
    SELECT MAX(sa.id) AS id, b.month, pin, sa.name, designation, grade, slab, employee_type, job_status, project_name, project_code, branch_name, branch_code, program_name, program_code, salary_to_be_paid 
    FROM payroll_tools.salary_info_history sa 
    INNER JOIN payroll_tools.batches ba ON ba.id = sa.batch_id 
    INNER JOIN payroll_tools.salary_months b ON b.id = ba.salary_month_id 
    WHERE sa.salary_to_be_paid != ''separation'' 
    AND ba.`type` = ''salary'' and sa.program_code = ''11''
    AND b.month IN (
            SELECT MAX(month) 
            FROM payroll_tools.batches bb 
            INNER JOIN payroll_tools.salary_months bm ON bm.id = bb.salary_month_id 
            WHERE salary_month_id IS NOT NULL 
            AND status IN (''finalized'',''archived'')
      )
    GROUP BY pin, b.month, sa.name, designation, grade, slab, employee_type, job_status, project_name, project_code, branch_name, branch_code, program_name, program_code, salary_to_be_paid
  ) s'
  ) k 
  LEFT JOIN HRReportDb.HRReportDb.dbo.tblERP_Data A ON staffpin = RIGHT(REPLICATE('0', 8) + k.pin,8) 
  LEFT JOIN (Select PIN,max(FDate) as FDate from [HRReportDB].[HRPMS].[dbo].[tblSlab] group by PIN) B ON A.[staffpin] = B.[PIN] 
  LEFT JOIN DataProject.bpm.Branch ON HR_BranchId = branch_code 
WHERE JobBase NOT IN ('REGULAR', 'SERVICE', 'CONTRACT');
import { DomainEntityService } from '../base/DomainEntityService';
import { Inventory } from './Inventory';
import { Service, Inject } from 'typedi';
import { FindOptions } from '../base/FindOptions';
import { Equal, In } from 'typeorm';
import { ConfigurationService } from '../configuration/ConfigurationService';
import { CustomerService } from '../customer/CustomerService';
import { PartnerService } from '../partner/PartnerService';
import { DynamicAttrsService } from '../attribute/DynamicAttrsService';
import { DataFileService } from '../datafile/DataFileService';
import { SubmissionPeriodService } from '../submission/SubmissionPeriodService';
import { InventoryQuantityService } from './InventoryQuantityService';
import { InventoryPriceService } from './InventoryPriceService';
import { Writer } from '../../writer/Writer';
import { InventoryInput } from './InventoryInput';
import { ServiceError } from '../base/ServiceError';
import { Operator } from '../base/filters/Operator';
import { AppDataSource } from '../../platform/DataSource';
import { InventoryResubmitInput } from './InventoryResubmitInput';

const INVENTORY_RESUBMIT = 'InventoryResubmit';

@Service()
export class InventoryService extends DomainEntityService<Inventory> {
  @Inject()
  protected configurationService: ConfigurationService;

  @Inject()
  protected customerService: CustomerService;

  @Inject()
  protected partnerService: PartnerService;

  @Inject()
  protected dynamicAttrsService: DynamicAttrsService;

  @Inject()
  protected dataFileService: DataFileService;

  @Inject()
  protected submissionPeriodService: SubmissionPeriodService;

  @Inject()
  protected inventoryQuantityService: InventoryQuantityService;

  @Inject()
  protected inventoryPriceService: InventoryPriceService;

  @Inject('Writer')
  protected writer: Writer;

  constructor() {
    super(Inventory);
  }

  getServiceName(): string {
    return 'Inventory';
  }

  async findInventory(
    customerId: string,
    partnerId: string,
    options: FindOptions = new FindOptions()
  ): Promise<Inventory[]> {
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    const config = await this.configurationService.getConfiguration(customerId);
    const { offset, limit, filters = {}, sort } = options;

    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return Promise.resolve([]);
    }
    const maxAge = config.get('nmiMaxAgeInDays', '30');
    const query = this.repository
      .createQueryBuilder('ili')
      .leftJoinAndSelect(
        'ili.dynamicAttrs',
        'da',
        '"da"."ATTRIBUTE_TYPE" = \'IL\' and "da"."CUSTOMER_SID" = "ili"."CUSTOMER_SID"'
      )
      .innerJoinAndSelect(
        'ili.dataFile',
        'df',
        '"df"."CUSTOMER_SID" = "ili"."CUSTOMER_SID"'
      )
      .leftJoinAndMapOne(
        'ili.onHandQuantity',
        'ili.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'ili.onOrderQuantity',
        'ili.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'ili.committedQuantity',
        'ili.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'ili.floatQuantity',
        'ili.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'ili.backorderedQuantity',
        'ili.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'ili.returnedQuantity',
        'ili.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'ili.inTransitQuantity',
        'ili.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'ili.inStockQuantity',
        'ili.inventoryQuantities',
        'inStockInvQuantity',
        '"inStockInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'IN STOCK\')'
      )
      .leftJoinAndMapOne(
        'ili.damagedQuantity',
        'ili.inventoryQuantities',
        'damagedInvQuantity',
        '"damagedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'DAMAGED\')'
      )
      .leftJoinAndMapOne(
        'ili.transferredQuantity',
        'ili.inventoryQuantities',
        'transferredInvQuantity',
        '"transferredInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSFERRED\')'
      )
      .leftJoinAndMapOne(
        'ili.unitPrice',
        'ili.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'REPORTED_PRICE\')'
      )
      .leftJoinAndMapOne(
        'ili.bestFitPrice',
        'ili.inventoryPrices',
        'bestFitInvPrice',
        '"bestFitInvPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'BEST_FIT_PRICE\')'
      )
      .leftJoinAndMapOne(
        'ili.invConvertedPrice',
        'ili.inventoryPrices',
        'invConvertedInvPrice',
        '"invConvertedInvPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'INV_CONVERTED\')'
      )
      .offset(offset)
      .limit(limit);
    // add filters for quantity filters
    let quantityFiltersMap = new Map();
    quantityFiltersMap.set('onOrderQuantity', 'onOrderInvQuantity');
    quantityFiltersMap.set('onHandQuantity', 'onHandInvQuantity');
    quantityFiltersMap.set('committedQuantity', 'committedInvQuantity');
    quantityFiltersMap.set('floatQuantity', 'floatInvQuantity');
    quantityFiltersMap.set('backorderedQuantity', 'backorderedInvQuantity');
    quantityFiltersMap.set('returnedQuantity', 'returnedInvQuantity');
    quantityFiltersMap.set('inTransitQuantity', 'inTransitInvQuantity');
    quantityFiltersMap.set('inStockQuantity', 'inStockInvQuantity');
    quantityFiltersMap.set('damagedQuantity', 'damagedInvQuantity');
    quantityFiltersMap.set('transferredQuantity', 'transferredInvQuantity');

    for (let [key, value] of quantityFiltersMap) {
      const quantityFilters = filters[key];
      delete filters[key];
      if (quantityFilters) {
        const quantityWhere =
          this.inventoryQuantityService.buildWhereExpression(
            quantityFilters,
            value
          );
        query.andWhere(quantityWhere);
      }
    }
    this.buildWhere(filters, query);
    query
      .andWhere('"ili"."INVENTORY_DATE" >= sysdate - ' + maxAge)
      .andWhere('"ili"."CUSTOMER_SID" = ' + customer.sid);
    // add order by
    this.addOrderBys(query, sort);
    if (!PartnerService.isAll(partnerId)) {
      query.andWhere('"df"."REPORTING_PARTNER_SID" = ' + partner.sid);
    }
    const inventory = await query.getMany();
    return this.loadIliValidationAggregation(inventory);
  }

  protected async loadIliValidationAggregation(
    inventory: Inventory[]
  ): Promise<Inventory[]> {
    if (inventory.length === 0) return inventory;

    // construct SQL
    const sql =
      ' select ' +
      ' ili.sid, count(iliv.inv_Line_item_sid) validationCodesCount ' +
      ' from inv_line_item ili ' +
      ' left join ili_validation iliv on iliv.inv_Line_item_sid =  ili.sid  ' +
      ' and iliv.customer_sid = ili.CUSTOMER_SID  ' +
      ' where ili.customer_sid = :customersid and ili.sid in (:iliSids)' +
      ' group by ili.sid ';

    // execute SQL
    var rows = new Map();
    for (var i = 0; i < inventory.length; i += 1000) {
      const iliSids = inventory.slice(i, i + 1000).map((s) => s.sid);
      //not a param to avoid bind var peaking
      var inListSql = sql.replace(
        ':customersid',
        inventory[0].customerSid.toString() as string
      );
      inListSql = inListSql.replace(':iliSids', iliSids.join(','));

      var results = await this.repository.query(inListSql);
      results.map((r) => {
        rows.set(r.SID, r.VALIDATIONCODESCOUNT);
      });
    }
    // map SQL result
    return inventory.map((inventory) => {
      inventory.validationCodesCount = rows.get(inventory.sid);
      return inventory;
    });
  }

  async findNeedCorrectionInventory(
    customerId: string,
    partnerId: string,
    options: FindOptions = new FindOptions()
  ): Promise<Inventory[]> {
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    const { offset, limit, filters = {}, sort } = options;

    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return Promise.resolve([]);
    }
    const config = await this.configurationService.getConfiguration(customerId);
    const queueName = config.get('needCorrectionInventoryQueueName', 'resign');
    const maxAge = config.get('nmiMaxAgeInDays', '30');

    const query = this.repository
      .createQueryBuilder('inv')
      .leftJoinAndSelect(
        'inv.dynamicAttrs',
        'da',
        '"da"."ATTRIBUTE_TYPE" = \'IL\' and "da"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .innerJoinAndSelect(
        'inv.dataFile',
        'df',
        '"df"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect(
        'inv.submissionPeriod',
        'sp',
        '"sp"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect(
        'sp.submissionSchedule',
        'ss',
        '"sp"."CUSTOMER_SID" = "ss"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect('ss.dataType', 'dt')
      .leftJoinAndSelect(
        'sp.submissionPeriodLineItemView',
        'spli',
        '"sp"."CUSTOMER_SID" = "spli"."CUSTOMER_SID"'
      )
      .leftJoinAndMapOne(
        'inv.onHandQuantity',
        'inv.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'inv.onOrderQuantity',
        'inv.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'inv.committedQuantity',
        'inv.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'inv.floatQuantity',
        'inv.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'inv.backorderedQuantity',
        'inv.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'inv.returnedQuantity',
        'inv.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'inv.inTransitQuantity',
        'inv.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'inv.unitPrice',
        'inv.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
      )
      .innerJoin(
        'PEH_INV_QUEUE',
        'piq',
        '"piq"."CLIENT_SKU" = "inv"."CLIENT_SKU" and "piq"."INV_LINE_ITEM_SID" = "inv"."SID" ' +
          'and "piq"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .offset(offset)
      .limit(limit);

    // add filters for quantity filters
    let quantityFiltersMap = new Map();
    quantityFiltersMap.set('onOrderQuantity', 'onOrderInvQuantity');
    quantityFiltersMap.set('onHandQuantity', 'onHandInvQuantity');
    quantityFiltersMap.set('committedQuantity', 'committedInvQuantity');
    quantityFiltersMap.set('floatQuantity', 'floatInvQuantity');
    quantityFiltersMap.set('backorderedQuantity', 'backorderedInvQuantity');
    quantityFiltersMap.set('returnedQuantity', 'returnedInvQuantity');
    quantityFiltersMap.set('inTransitQuantity', 'inTransitInvQuantity');

    for (let [key, value] of quantityFiltersMap) {
      const quantityFilters = filters[key];
      delete filters[key];
      if (quantityFilters) {
        const quantityWhere =
          this.inventoryQuantityService.buildWhereExpression(
            quantityFilters,
            value
          );
        query.andWhere(quantityWhere);
      }
    }

    // add unit price condition
    const unitPriceFilters = filters['unitPrice'];
    delete filters['unitPrice'];
    if (unitPriceFilters) {
      const unitPriceWhere = this.inventoryPriceService.buildWhereExpression(
        unitPriceFilters,
        'invPrice'
      );
      query.andWhere(unitPriceWhere);
    }

    this.buildWhere(filters, query);
    query
      .andWhere('"inv"."CUSTOMER_SID" = ' + customer.sid)
      .andWhere('"inv"."INVENTORY_DATE" >= sysdate - ' + maxAge)
      .andWhere('"piq"."QUEUE_NAME" = \'' + queueName + "'")
      .andWhere({ deleted: Equal(0) });

    if (!PartnerService.isAll(partnerId)) {
      query.andWhere('"inv"."REPORTING_PARTNER_SID" = ' + partner.sid);
    }

    // add order by
    this.addOrderBys(query, sort);

    return query.getMany();
  }

  async resubmitInventory(
    customerId: string,
    partnerId: string,
    inputs: Partial<InventoryResubmitInput>[]
  ): Promise<ServiceError[]> {
    // Check input array size
    if (inputs.length === 0) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          'No inventory items provided for resubmission.'
        )
      ];
    }
    //Inventory Item Limit Check
    if (inputs.length > 1000) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          `The number of inventory items exceeds the limit of 1000.`
        )
      ];
    }
    const sids: number[] = inputs.map((input: InventoryInput) => {
      return input.sid;
    });
    let errors: ServiceError[] = [];

    // Check if partner has access to inventory items
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          `Customer ${customerId} or partner ${partnerId} are invalid.`
        )
      ];
    }

    // Query to fetch inventory items with required joins

    let inventoryItems: Inventory[] = await this.repository
      .createQueryBuilder('inv')
      .innerJoinAndSelect('inv.reportingPartner', 'rp')
      .leftJoinAndSelect('rp.gsNumbers', 'gs')
      .leftJoinAndSelect('inv.dynamicAttrs', 'da')
      .leftJoinAndMapOne(
        'inv.onHandQuantity',
        'inv.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'inv.onOrderQuantity',
        'inv.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'inv.committedQuantity',
        'inv.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'inv.floatQuantity',
        'inv.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'inv.backorderedQuantity',
        'inv.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'inv.returnedQuantity',
        'inv.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'inv.inTransitQuantity',
        'inv.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'inv.unitPrice',
        'inv.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = (SELECT SID FROM PRICE_TYPE WHERE NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
      )
      .where({ sid: In(sids) })
      .andWhere('"gs"."CUSTOMER_SID" = :customerSid', {
        customerSid: customer.sid
      })
      .getMany();

    let inventoryItemMap = new Map(
      inventoryItems.map((inventoryItem) => [
        Number(inventoryItem.sid),
        inventoryItem
      ])
    );

    let inventoryItemsToResubmit = [];
    errors = await Promise.all(
      inputs.map(async (inventoryResubmitInput) => {
        const inventoryItem = inventoryItemMap.get(
          Number(inventoryResubmitInput.sid)
        );
        if (inventoryItem) {
          let resubmitInventoryItem =
            await this.loadResubmitInventoryLineItemRefs(
              inventoryItem,
              inventoryResubmitInput
            );
          inventoryItemsToResubmit.push(resubmitInventoryItem);
          return null;
        } else {
          return new ServiceError(
            'INVENTORY_ITEM_ERR',
            `Inventory Item with IID ${inventoryResubmitInput.sid} not found. Please check the input data.`
          );
        }
      })
    );

    let reportingPartnerId: string;

    if(inventoryItemsToResubmit.length != 0) {
        reportingPartnerId =  inventoryItemsToResubmit[0].reportingPartner.id;
    }
    
    // Write to S3 or perform further operations with resubmitted inventory items
    console.log(
      `Inventory items to resubmit: ${inventoryItemsToResubmit.length}`
    );
    if (inventoryItemsToResubmit.length > 0) {
      // Example: Write to a service or perform operations with resubmitted inventory items
      await this.writer.writeToPartnersFolder(
        customerId,
        reportingPartnerId,
        INVENTORY_RESUBMIT,
        inventoryItemsToResubmit
      );
    }

    return errors;
  }

  clone(...objs) {
    return Object.assign({}, ...objs);
  }

  async loadResubmitInventoryLineItemRefs(
    inventoryItem: Inventory,
    inventoryResubmitInput: Partial<InventoryResubmitInput>
  ): Promise<Inventory> {
    let resubmitInventoryLineItem: Inventory = this.clone(
      inventoryItem,
      inventoryResubmitInput
    );

    resubmitInventoryLineItem.dynamicAttrs = this.clone(
      await inventoryItem.dynamicAttrs,
      inventoryResubmitInput.dynamicAttrs
    );

    resubmitInventoryLineItem.onHandQuantity = this.clone(
      inventoryItem.onHandQuantity,
      inventoryResubmitInput.onHandQuantity
    );

    resubmitInventoryLineItem.onOrderQuantity = this.clone(
      inventoryItem.onOrderQuantity,
      inventoryResubmitInput.onOrderQuantity
    );

    resubmitInventoryLineItem.committedQuantity = this.clone(
      inventoryItem.committedQuantity,
      inventoryResubmitInput.committedQuantity
    );

    resubmitInventoryLineItem.backorderedQuantity = this.clone(
      inventoryItem.backorderedQuantity,
      inventoryResubmitInput.backorderedQuantity
    );

    resubmitInventoryLineItem.returnedQuantity = this.clone(
      inventoryItem.returnedQuantity,
      inventoryResubmitInput.returnedQuantity
    );

    resubmitInventoryLineItem.inTransitQuantity = this.clone(
      inventoryItem.inTransitQuantity,
      inventoryResubmitInput.inTransitQuantity
    );

    resubmitInventoryLineItem.inStockQuantity = this.clone(
      inventoryItem.inStockQuantity,
      inventoryResubmitInput.inStockQuantity
    );

    resubmitInventoryLineItem.damagedQuantity = this.clone(
      inventoryItem.damagedQuantity,
      inventoryResubmitInput.damagedQuantity
    );

    resubmitInventoryLineItem.transferredQuantity = this.clone(
      inventoryItem.transferredQuantity,
      inventoryResubmitInput.transferredQuantity
    );

    resubmitInventoryLineItem.inventoryPrices = this.clone(
      await inventoryItem.inventoryPrices
    );

    resubmitInventoryLineItem.reportedPrice = this.clone(
      inventoryItem.reportedPrice,
      inventoryResubmitInput.reportedPrice
    );

    resubmitInventoryLineItem.reportingPartner = this.clone(
      await inventoryItem.reportingPartner
    );

    return resubmitInventoryLineItem;
  }

  async updateInventory(
    customerId: string,
    partnerId: string,
    data: InventoryInput[]
  ): Promise<ServiceError[]> {
    let inventoryToCreate: Inventory[] = [];
    let sids: number[] = data.map((input: InventoryInput) => {
      return input.sid;
    });
    let errors: ServiceError[] = [];

    // Check if partner has access to ili
    let options: FindOptions = {
      offset: 0,
      limit: 1000,
      filters: {
        sid: {
          operator: Operator.IN,
          values: sids
        }
      }
    };
    let inventory: Inventory[] = await this.findNeedCorrectionInventory(
      customerId,
      partnerId,
      options
    );

    let sidsToUpdate: number[] = [];
    errors = sids.map((sid) => {
      if (inventory) {
        let s: Inventory = inventory.find((inv: Inventory) => {
          return inv.sid.toString() === sid.toString();
        });
        if (s) {
          sidsToUpdate.push(sid);
          return null;
        }
      }
      return new ServiceError(
        'INVENTORY_NOT_FOUND',
        `Inventory line sid : ${sid} not found`
      );
    });

    if (inventory && inventory.length > 0) {
      let invLineItems: Inventory[] = await this.repository
        .createQueryBuilder('inv')
        // load partner and gs number
        .innerJoinAndSelect('inv.reportingPartner', 'rp')
        .leftJoinAndSelect('rp.gsNumbers', 'gs')
        // load dynamic attrs
        .leftJoinAndSelect('inv.dynamicAttrs', 'da')
        .leftJoinAndMapOne(
          'inv.onHandQuantity',
          'inv.inventoryQuantities',
          'onHandInvQuantity',
          '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
        )
        .leftJoinAndMapOne(
          'inv.onOrderQuantity',
          'inv.inventoryQuantities',
          'onOrderInvQuantity',
          '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
        )
        .leftJoinAndMapOne(
          'inv.committedQuantity',
          'inv.inventoryQuantities',
          'committedInvQuantity',
          '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
        )
        .leftJoinAndMapOne(
          'inv.floatQuantity',
          'inv.inventoryQuantities',
          'floatInvQuantity',
          '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
        )
        .leftJoinAndMapOne(
          'inv.backorderedQuantity',
          'inv.inventoryQuantities',
          'backorderedInvQuantity',
          '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
        )
        .leftJoinAndMapOne(
          'inv.returnedQuantity',
          'inv.inventoryQuantities',
          'returnedInvQuantity',
          '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
        )
        .leftJoinAndMapOne(
          'inv.inTransitQuantity',
          'inv.inventoryQuantities',
          'inTransitInvQuantity',
          '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
        )
        .leftJoinAndMapOne(
          'inv.unitPrice',
          'inv.inventoryPrices',
          'invPrice',
          '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
        )
        .where({ sid: In(sidsToUpdate) })
        .andWhere('"gs"."CUSTOMER_SID" = :customerSid', {
          customerSid: inventory[0].customerSid
        })
        .getMany();

      await Promise.all(
        invLineItems.map(async (invLineItem) => {
          // load serial numbers

          let input: InventoryInput = data.find((inv: InventoryInput) => {
            return inv.sid.toString() === invLineItem.sid.toString();
          });

          invLineItem = Object.assign({}, invLineItem, input);

          // add inv line to list to write to S3.
          inventoryToCreate.push(invLineItem);
        })
      );

      console.log(`Inventory to write to S3 : ${inventoryToCreate.length}`);
      // Write to S3
      if (inventoryToCreate.length > 0) {
        await this.writer.write(
          customerId,
          partnerId,
          this.getServiceName(),
          inventoryToCreate
        );
      }

      // It is safe to delete from PEH after writing to S3, just in case if delete fails,
      // the inv will still be deleted from PEH the json file from S3 is loaded into nucleus.
      // If we delete first and for some reason the writing to S3 fails, there is no way to roll back the db update.
      await Promise.all(
        inventoryToCreate.map(async (invLineItem) => {
          try {
            // delete inventory line from PEH
            await AppDataSource.query(
              'delete from peh_inv_queue where inv_line_item_sid = :sid',
              [invLineItem.sid]
            );
            console.log(`Deleted line item : ${invLineItem.sid} from PEH`);
          } catch (e) {
            console.log(
              `Error while deleting from PEH for inv line : ${JSON.stringify(
                invLineItem
              )} for Customer : ${customerId}`
            );
            console.error(e);
            // eat the error as if delete fails, the only con is User will still be able to see the inv line
            // until the file on S3 is loaded into nucleus
          }
        })
      );
    }

    return errors;
  }
}
import { DomainEntityService } from '../base/DomainEntityService';
import { Inventory } from './Inventory';
import { Service, Inject } from 'typedi';
import { FindOptions } from '../base/FindOptions';
import { Equal, In } from 'typeorm';
import { ConfigurationService } from '../configuration/ConfigurationService';
import { CustomerService } from '../customer/CustomerService';
import { PartnerService } from '../partner/PartnerService';
import { DynamicAttrsService } from '../attribute/DynamicAttrsService';
import { DataFileService } from '../datafile/DataFileService';
import { SubmissionPeriodService } from '../submission/SubmissionPeriodService';
import { InventoryQuantityService } from './InventoryQuantityService';
import { InventoryPriceService } from './InventoryPriceService';
import { Writer } from '../../writer/Writer';
import { InventoryInput } from './InventoryInput';
import { ServiceError } from '../base/ServiceError';
import { Operator } from '../base/filters/Operator';
import { AppDataSource } from '../../platform/DataSource';
import { InventoryResubmitInput } from './InventoryResubmitInput';

const INVENTORY_RESUBMIT = 'InventoryResubmit';

@Service()
export class InventoryService extends DomainEntityService<Inventory> {
  @Inject()
  protected configurationService: ConfigurationService;

  @Inject()
  protected customerService: CustomerService;

  @Inject()
  protected partnerService: PartnerService;

  @Inject()
  protected dynamicAttrsService: DynamicAttrsService;

  @Inject()
  protected dataFileService: DataFileService;

  @Inject()
  protected submissionPeriodService: SubmissionPeriodService;

  @Inject()
  protected inventoryQuantityService: InventoryQuantityService;

  @Inject()
  protected inventoryPriceService: InventoryPriceService;

  @Inject('Writer')
  protected writer: Writer;

  constructor() {
    super(Inventory);
  }

  getServiceName(): string {
    return 'Inventory';
  }

  async findInventory(
    customerId: string,
    partnerId: string,
    options: FindOptions = new FindOptions()
  ): Promise<Inventory[]> {
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    const config = await this.configurationService.getConfiguration(customerId);
    const { offset, limit, filters = {}, sort } = options;

    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return Promise.resolve([]);
    }
    const maxAge = config.get('nmiMaxAgeInDays', '30');
    const query = this.repository
      .createQueryBuilder('ili')
      .leftJoinAndSelect(
        'ili.dynamicAttrs',
        'da',
        '"da"."ATTRIBUTE_TYPE" = \'IL\' and "da"."CUSTOMER_SID" = "ili"."CUSTOMER_SID"'
      )
      .innerJoinAndSelect(
        'ili.dataFile',
        'df',
        '"df"."CUSTOMER_SID" = "ili"."CUSTOMER_SID"'
      )
      .leftJoinAndMapOne(
        'ili.onHandQuantity',
        'ili.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'ili.onOrderQuantity',
        'ili.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'ili.committedQuantity',
        'ili.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'ili.floatQuantity',
        'ili.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'ili.backorderedQuantity',
        'ili.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'ili.returnedQuantity',
        'ili.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'ili.inTransitQuantity',
        'ili.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'ili.inStockQuantity',
        'ili.inventoryQuantities',
        'inStockInvQuantity',
        '"inStockInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'IN STOCK\')'
      )
      .leftJoinAndMapOne(
        'ili.damagedQuantity',
        'ili.inventoryQuantities',
        'damagedInvQuantity',
        '"damagedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'DAMAGED\')'
      )
      .leftJoinAndMapOne(
        'ili.transferredQuantity',
        'ili.inventoryQuantities',
        'transferredInvQuantity',
        '"transferredInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSFERRED\')'
      )
      .leftJoinAndMapOne(
        'ili.unitPrice',
        'ili.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'REPORTED_PRICE\')'
      )
      .leftJoinAndMapOne(
        'ili.bestFitPrice',
        'ili.inventoryPrices',
        'bestFitInvPrice',
        '"bestFitInvPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'BEST_FIT_PRICE\')'
      )
      .leftJoinAndMapOne(
        'ili.invConvertedPrice',
        'ili.inventoryPrices',
        'invConvertedInvPrice',
        '"invConvertedInvPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'INV_CONVERTED\')'
      )
      .offset(offset)
      .limit(limit);
    // add filters for quantity filters
    let quantityFiltersMap = new Map();
    quantityFiltersMap.set('onOrderQuantity', 'onOrderInvQuantity');
    quantityFiltersMap.set('onHandQuantity', 'onHandInvQuantity');
    quantityFiltersMap.set('committedQuantity', 'committedInvQuantity');
    quantityFiltersMap.set('floatQuantity', 'floatInvQuantity');
    quantityFiltersMap.set('backorderedQuantity', 'backorderedInvQuantity');
    quantityFiltersMap.set('returnedQuantity', 'returnedInvQuantity');
    quantityFiltersMap.set('inTransitQuantity', 'inTransitInvQuantity');
    quantityFiltersMap.set('inStockQuantity', 'inStockInvQuantity');
    quantityFiltersMap.set('damagedQuantity', 'damagedInvQuantity');
    quantityFiltersMap.set('transferredQuantity', 'transferredInvQuantity');

    for (let [key, value] of quantityFiltersMap) {
      const quantityFilters = filters[key];
      delete filters[key];
      if (quantityFilters) {
        const quantityWhere =
          this.inventoryQuantityService.buildWhereExpression(
            quantityFilters,
            value
          );
        query.andWhere(quantityWhere);
      }
    }
    this.buildWhere(filters, query);
    query
      .andWhere('"ili"."INVENTORY_DATE" >= sysdate - ' + maxAge)
      .andWhere('"ili"."CUSTOMER_SID" = ' + customer.sid);
    // add order by
    this.addOrderBys(query, sort);
    if (!PartnerService.isAll(partnerId)) {
      query.andWhere('"df"."REPORTING_PARTNER_SID" = ' + partner.sid);
    }
    const inventory = await query.getMany();
    return this.loadIliValidationAggregation(inventory);
  }

  protected async loadIliValidationAggregation(
    inventory: Inventory[]
  ): Promise<Inventory[]> {
    if (inventory.length === 0) return inventory;

    // construct SQL
    const sql =
      ' select ' +
      ' ili.sid, count(iliv.inv_Line_item_sid) validationCodesCount ' +
      ' from inv_line_item ili ' +
      ' left join ili_validation iliv on iliv.inv_Line_item_sid =  ili.sid  ' +
      ' and iliv.customer_sid = ili.CUSTOMER_SID  ' +
      ' where ili.customer_sid = :customersid and ili.sid in (:iliSids)' +
      ' group by ili.sid ';

    // execute SQL
    var rows = new Map();
    for (var i = 0; i < inventory.length; i += 1000) {
      const iliSids = inventory.slice(i, i + 1000).map((s) => s.sid);
      //not a param to avoid bind var peaking
      var inListSql = sql.replace(
        ':customersid',
        inventory[0].customerSid.toString() as string
      );
      inListSql = inListSql.replace(':iliSids', iliSids.join(','));

      var results = await this.repository.query(inListSql);
      results.map((r) => {
        rows.set(r.SID, r.VALIDATIONCODESCOUNT);
      });
    }
    // map SQL result
    return inventory.map((inventory) => {
      inventory.validationCodesCount = rows.get(inventory.sid);
      return inventory;
    });
  }

  async findNeedCorrectionInventory(
    customerId: string,
    partnerId: string,
    options: FindOptions = new FindOptions()
  ): Promise<Inventory[]> {
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    const { offset, limit, filters = {}, sort } = options;

    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return Promise.resolve([]);
    }
    const config = await this.configurationService.getConfiguration(customerId);
    const queueName = config.get('needCorrectionInventoryQueueName', 'resign');
    const maxAge = config.get('nmiMaxAgeInDays', '30');

    const query = this.repository
      .createQueryBuilder('inv')
      .leftJoinAndSelect(
        'inv.dynamicAttrs',
        'da',
        '"da"."ATTRIBUTE_TYPE" = \'IL\' and "da"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .innerJoinAndSelect(
        'inv.dataFile',
        'df',
        '"df"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect(
        'inv.submissionPeriod',
        'sp',
        '"sp"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect(
        'sp.submissionSchedule',
        'ss',
        '"sp"."CUSTOMER_SID" = "ss"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect('ss.dataType', 'dt')
      .leftJoinAndSelect(
        'sp.submissionPeriodLineItemView',
        'spli',
        '"sp"."CUSTOMER_SID" = "spli"."CUSTOMER_SID"'
      )
      .leftJoinAndMapOne(
        'inv.onHandQuantity',
        'inv.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'inv.onOrderQuantity',
        'inv.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'inv.committedQuantity',
        'inv.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'inv.floatQuantity',
        'inv.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'inv.backorderedQuantity',
        'inv.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'inv.returnedQuantity',
        'inv.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'inv.inTransitQuantity',
        'inv.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'inv.unitPrice',
        'inv.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
      )
      .innerJoin(
        'PEH_INV_QUEUE',
        'piq',
        '"piq"."CLIENT_SKU" = "inv"."CLIENT_SKU" and "piq"."INV_LINE_ITEM_SID" = "inv"."SID" ' +
          'and "piq"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .offset(offset)
      .limit(limit);

    // add filters for quantity filters
    let quantityFiltersMap = new Map();
    quantityFiltersMap.set('onOrderQuantity', 'onOrderInvQuantity');
    quantityFiltersMap.set('onHandQuantity', 'onHandInvQuantity');
    quantityFiltersMap.set('committedQuantity', 'committedInvQuantity');
    quantityFiltersMap.set('floatQuantity', 'floatInvQuantity');
    quantityFiltersMap.set('backorderedQuantity', 'backorderedInvQuantity');
    quantityFiltersMap.set('returnedQuantity', 'returnedInvQuantity');
    quantityFiltersMap.set('inTransitQuantity', 'inTransitInvQuantity');

    for (let [key, value] of quantityFiltersMap) {
      const quantityFilters = filters[key];
      delete filters[key];
      if (quantityFilters) {
        const quantityWhere =
          this.inventoryQuantityService.buildWhereExpression(
            quantityFilters,
            value
          );
        query.andWhere(quantityWhere);
      }
    }

    // add unit price condition
    const unitPriceFilters = filters['unitPrice'];
    delete filters['unitPrice'];
    if (unitPriceFilters) {
      const unitPriceWhere = this.inventoryPriceService.buildWhereExpression(
        unitPriceFilters,
        'invPrice'
      );
      query.andWhere(unitPriceWhere);
    }

    this.buildWhere(filters, query);
    query
      .andWhere('"inv"."CUSTOMER_SID" = ' + customer.sid)
      .andWhere('"inv"."INVENTORY_DATE" >= sysdate - ' + maxAge)
      .andWhere('"piq"."QUEUE_NAME" = \'' + queueName + "'")
      .andWhere({ deleted: Equal(0) });

    if (!PartnerService.isAll(partnerId)) {
      query.andWhere('"inv"."REPORTING_PARTNER_SID" = ' + partner.sid);
    }

    // add order by
    this.addOrderBys(query, sort);

    return query.getMany();
  }

  async resubmitInventory(
    customerId: string,
    partnerId: string,
    inputs: Partial<InventoryResubmitInput>[]
  ): Promise<ServiceError[]> {
    // Check input array size
    if (inputs.length === 0) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          'No inventory items provided for resubmission.'
        )
      ];
    }
    //Inventory Item Limit Check
    if (inputs.length > 1000) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          `The number of inventory items exceeds the limit of 1000.`
        )
      ];
    }
    const sids: number[] = inputs.map((input: InventoryInput) => {
      return input.sid;
    });
    let errors: ServiceError[] = [];

    // Check if partner has access to inventory items
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          `Customer ${customerId} or partner ${partnerId} are invalid.`
        )
      ];
    }

    // Query to fetch inventory items with required joins

    let inventoryItems: Inventory[] = await this.repository
      .createQueryBuilder('inv')
      .innerJoinAndSelect('inv.reportingPartner', 'rp')
      .leftJoinAndSelect('rp.gsNumbers', 'gs')
      .leftJoinAndSelect('inv.dynamicAttrs', 'da')
      .leftJoinAndMapOne(
        'inv.onHandQuantity',
        'inv.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'inv.onOrderQuantity',
        'inv.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'inv.committedQuantity',
        'inv.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'inv.floatQuantity',
        'inv.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'inv.backorderedQuantity',
        'inv.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'inv.returnedQuantity',
        'inv.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'inv.inTransitQuantity',
        'inv.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'inv.unitPrice',
        'inv.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = (SELECT SID FROM PRICE_TYPE WHERE NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
      )
      .where({ sid: In(sids) })
      .andWhere('"gs"."CUSTOMER_SID" = :customerSid', {
        customerSid: customer.sid
      })
      .getMany();

    let inventoryItemMap = new Map(
      inventoryItems.map((inventoryItem) => [
        Number(inventoryItem.sid),
        inventoryItem
      ])
    );

    let inventoryItemsToResubmit = [];
    errors = await Promise.all(
      inputs.map(async (inventoryResubmitInput) => {
        const inventoryItem = inventoryItemMap.get(
          Number(inventoryResubmitInput.sid)
        );
        if (inventoryItem) {
          let resubmitInventoryItem =
            await this.loadResubmitInventoryLineItemRefs(
              inventoryItem,
              inventoryResubmitInput
            );
          inventoryItemsToResubmit.push(resubmitInventoryItem);
          return null;
        } else {
          return new ServiceError(
            'INVENTORY_ITEM_ERR',
            `Inventory Item with IID ${inventoryResubmitInput.sid} not found. Please check the input data.`
          );
        }
      })
    );

let reportingPartnerId: string;
// Process each SID
errors = await Promise.all(
  sids.map(async (sid) => {
    // Log the SID being processed
    console.log('Processing SID:', sid);

    // Query the database
    let partnerIdResult = await this.repository
      .createQueryBuilder('ili')
      .innerJoin('ili.reportingPartner', 'rp')
      .innerJoin('ili.customer', 'c')
      .where('ili.sid = :sid', { sid }) // Use `sid` directly
      .andWhere('ili.CUSTOMER_SID = :customerSid', { customerSid: customer.sid })
      .select('rp.id')
      .getRawOne();

    // Check query result and handle errors
    if (partnerIdResult) {
      reportingPartnerId = String(partnerIdResult['rp_ID']);
      // Optionally process `reportingPartnerId` if needed
      return null; // No error
    } else {
      console.log('No reporting partner ID found for SID:', sid);
      return new ServiceError(
        'INVENTORY_ITEM_ERR',
        `Inventory Item with IID ${sid} not found. Please check the input data again.`
      );
    }
  })
);
    
    // Write to S3 or perform further operations with resubmitted inventory items
    console.log(
      `Inventory items to resubmit: ${inventoryItemsToResubmit.length}`
    );
    if (inventoryItemsToResubmit.length > 0) {
      // Example: Write to a service or perform operations with resubmitted inventory items
      await this.writer.writeToPartnersFolder(
        customerId,
        reportingPartnerId,
        INVENTORY_RESUBMIT,
        inventoryItemsToResubmit
      );
    }

    return errors;
  }

  clone(...objs) {
    return Object.assign({}, ...objs);
  }

  async loadResubmitInventoryLineItemRefs(
    inventoryItem: Inventory,
    inventoryResubmitInput: Partial<InventoryResubmitInput>
  ): Promise<Inventory> {
    let resubmitInventoryLineItem: Inventory = this.clone(
      inventoryItem,
      inventoryResubmitInput
    );

    resubmitInventoryLineItem.dynamicAttrs = this.clone(
      await inventoryItem.dynamicAttrs,
      inventoryResubmitInput.dynamicAttrs
    );

    resubmitInventoryLineItem.onHandQuantity = this.clone(
      inventoryItem.onHandQuantity,
      inventoryResubmitInput.onHandQuantity
    );

    resubmitInventoryLineItem.onOrderQuantity = this.clone(
      inventoryItem.onOrderQuantity,
      inventoryResubmitInput.onOrderQuantity
    );

    resubmitInventoryLineItem.committedQuantity = this.clone(
      inventoryItem.committedQuantity,
      inventoryResubmitInput.committedQuantity
    );

    resubmitInventoryLineItem.backorderedQuantity = this.clone(
      inventoryItem.backorderedQuantity,
      inventoryResubmitInput.backorderedQuantity
    );

    resubmitInventoryLineItem.returnedQuantity = this.clone(
      inventoryItem.returnedQuantity,
      inventoryResubmitInput.returnedQuantity
    );

    resubmitInventoryLineItem.inTransitQuantity = this.clone(
      inventoryItem.inTransitQuantity,
      inventoryResubmitInput.inTransitQuantity
    );

    resubmitInventoryLineItem.inStockQuantity = this.clone(
      inventoryItem.inStockQuantity,
      inventoryResubmitInput.inStockQuantity
    );

    resubmitInventoryLineItem.damagedQuantity = this.clone(
      inventoryItem.damagedQuantity,
      inventoryResubmitInput.damagedQuantity
    );

    resubmitInventoryLineItem.transferredQuantity = this.clone(
      inventoryItem.transferredQuantity,
      inventoryResubmitInput.transferredQuantity
    );

    resubmitInventoryLineItem.inventoryPrices = this.clone(
      await inventoryItem.inventoryPrices
    );

    resubmitInventoryLineItem.reportedPrice = this.clone(
      inventoryItem.reportedPrice,
      inventoryResubmitInput.reportedPrice
    );

    resubmitInventoryLineItem.reportingPartner = this.clone(
      await inventoryItem.reportingPartner
    );

    return resubmitInventoryLineItem;
  }

  async updateInventory(
    customerId: string,
    partnerId: string,
    data: InventoryInput[]
  ): Promise<ServiceError[]> {
    let inventoryToCreate: Inventory[] = [];
    let sids: number[] = data.map((input: InventoryInput) => {
      return input.sid;
    });
    let errors: ServiceError[] = [];

    // Check if partner has access to ili
    let options: FindOptions = {
      offset: 0,
      limit: 1000,
      filters: {
        sid: {
          operator: Operator.IN,
          values: sids
        }
      }
    };
    let inventory: Inventory[] = await this.findNeedCorrectionInventory(
      customerId,
      partnerId,
      options
    );

    let sidsToUpdate: number[] = [];
    errors = sids.map((sid) => {
      if (inventory) {
        let s: Inventory = inventory.find((inv: Inventory) => {
          return inv.sid.toString() === sid.toString();
        });
        if (s) {
          sidsToUpdate.push(sid);
          return null;
        }
      }
      return new ServiceError(
        'INVENTORY_NOT_FOUND',
        `Inventory line sid : ${sid} not found`
      );
    });

    if (inventory && inventory.length > 0) {
      let invLineItems: Inventory[] = await this.repository
        .createQueryBuilder('inv')
        // load partner and gs number
        .innerJoinAndSelect('inv.reportingPartner', 'rp')
        .leftJoinAndSelect('rp.gsNumbers', 'gs')
        // load dynamic attrs
        .leftJoinAndSelect('inv.dynamicAttrs', 'da')
        .leftJoinAndMapOne(
          'inv.onHandQuantity',
          'inv.inventoryQuantities',
          'onHandInvQuantity',
          '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
        )
        .leftJoinAndMapOne(
          'inv.onOrderQuantity',
          'inv.inventoryQuantities',
          'onOrderInvQuantity',
          '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
        )
        .leftJoinAndMapOne(
          'inv.committedQuantity',
          'inv.inventoryQuantities',
          'committedInvQuantity',
          '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
        )
        .leftJoinAndMapOne(
          'inv.floatQuantity',
          'inv.inventoryQuantities',
          'floatInvQuantity',
          '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
        )
        .leftJoinAndMapOne(
          'inv.backorderedQuantity',
          'inv.inventoryQuantities',
          'backorderedInvQuantity',
          '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
        )
        .leftJoinAndMapOne(
          'inv.returnedQuantity',
          'inv.inventoryQuantities',
          'returnedInvQuantity',
          '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
        )
        .leftJoinAndMapOne(
          'inv.inTransitQuantity',
          'inv.inventoryQuantities',
          'inTransitInvQuantity',
          '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
        )
        .leftJoinAndMapOne(
          'inv.unitPrice',
          'inv.inventoryPrices',
          'invPrice',
          '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
        )
        .where({ sid: In(sidsToUpdate) })
        .andWhere('"gs"."CUSTOMER_SID" = :customerSid', {
          customerSid: inventory[0].customerSid
        })
        .getMany();

      await Promise.all(
        invLineItems.map(async (invLineItem) => {
          // load serial numbers

          let input: InventoryInput = data.find((inv: InventoryInput) => {
            return inv.sid.toString() === invLineItem.sid.toString();
          });

          invLineItem = Object.assign({}, invLineItem, input);

          // add inv line to list to write to S3.
          inventoryToCreate.push(invLineItem);
        })
      );

      console.log(`Inventory to write to S3 : ${inventoryToCreate.length}`);
      // Write to S3
      if (inventoryToCreate.length > 0) {
        await this.writer.write(
          customerId,
          partnerId,
          this.getServiceName(),
          inventoryToCreate
        );
      }

      // It is safe to delete from PEH after writing to S3, just in case if delete fails,
      // the inv will still be deleted from PEH the json file from S3 is loaded into nucleus.
      // If we delete first and for some reason the writing to S3 fails, there is no way to roll back the db update.
      await Promise.all(
        inventoryToCreate.map(async (invLineItem) => {
          try {
            // delete inventory line from PEH
            await AppDataSource.query(
              'delete from peh_inv_queue where inv_line_item_sid = :sid',
              [invLineItem.sid]
            );
            console.log(`Deleted line item : ${invLineItem.sid} from PEH`);
          } catch (e) {
            console.log(
              `Error while deleting from PEH for inv line : ${JSON.stringify(
                invLineItem
              )} for Customer : ${customerId}`
            );
            console.error(e);
            // eat the error as if delete fails, the only con is User will still be able to see the inv line
            // until the file on S3 is loaded into nucleus
          }
        })
      );
    }

    return errors;
  }
}
import { DomainEntityService } from '../base/DomainEntityService';
import { Inventory } from './Inventory';
import { Service, Inject } from 'typedi';
import { FindOptions } from '../base/FindOptions';
import { Equal, In } from 'typeorm';
import { ConfigurationService } from '../configuration/ConfigurationService';
import { CustomerService } from '../customer/CustomerService';
import { PartnerService } from '../partner/PartnerService';
import { DynamicAttrsService } from '../attribute/DynamicAttrsService';
import { DataFileService } from '../datafile/DataFileService';
import { SubmissionPeriodService } from '../submission/SubmissionPeriodService';
import { InventoryQuantityService } from './InventoryQuantityService';
import { InventoryPriceService } from './InventoryPriceService';
import { Writer } from '../../writer/Writer';
import { InventoryInput } from './InventoryInput';
import { ServiceError } from '../base/ServiceError';
import { Operator } from '../base/filters/Operator';
import { AppDataSource } from '../../platform/DataSource';
import { InventoryResubmitInput } from './InventoryResubmitInput';

const INVENTORY_RESUBMIT = 'InventoryResubmit';

@Service()
export class InventoryService extends DomainEntityService<Inventory> {
  @Inject()
  protected configurationService: ConfigurationService;

  @Inject()
  protected customerService: CustomerService;

  @Inject()
  protected partnerService: PartnerService;

  @Inject()
  protected dynamicAttrsService: DynamicAttrsService;

  @Inject()
  protected dataFileService: DataFileService;

  @Inject()
  protected submissionPeriodService: SubmissionPeriodService;

  @Inject()
  protected inventoryQuantityService: InventoryQuantityService;

  @Inject()
  protected inventoryPriceService: InventoryPriceService;

  @Inject('Writer')
  protected writer: Writer;

  constructor() {
    super(Inventory);
  }

  getServiceName(): string {
    return 'Inventory';
  }

  async findInventory(
    customerId: string,
    partnerId: string,
    options: FindOptions = new FindOptions()
  ): Promise<Inventory[]> {
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    const config = await this.configurationService.getConfiguration(customerId);
    const { offset, limit, filters = {}, sort } = options;

    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return Promise.resolve([]);
    }
    const maxAge = config.get('nmiMaxAgeInDays', '30');
    const query = this.repository
      .createQueryBuilder('ili')
      .leftJoinAndSelect(
        'ili.dynamicAttrs',
        'da',
        '"da"."ATTRIBUTE_TYPE" = \'IL\' and "da"."CUSTOMER_SID" = "ili"."CUSTOMER_SID"'
      )
      .innerJoinAndSelect(
        'ili.dataFile',
        'df',
        '"df"."CUSTOMER_SID" = "ili"."CUSTOMER_SID"'
      )
      .leftJoinAndMapOne(
        'ili.onHandQuantity',
        'ili.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'ili.onOrderQuantity',
        'ili.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'ili.committedQuantity',
        'ili.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'ili.floatQuantity',
        'ili.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'ili.backorderedQuantity',
        'ili.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'ili.returnedQuantity',
        'ili.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'ili.inTransitQuantity',
        'ili.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'ili.inStockQuantity',
        'ili.inventoryQuantities',
        'inStockInvQuantity',
        '"inStockInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'IN STOCK\')'
      )
      .leftJoinAndMapOne(
        'ili.damagedQuantity',
        'ili.inventoryQuantities',
        'damagedInvQuantity',
        '"damagedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'DAMAGED\')'
      )
      .leftJoinAndMapOne(
        'ili.transferredQuantity',
        'ili.inventoryQuantities',
        'transferredInvQuantity',
        '"transferredInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSFERRED\')'
      )
      .leftJoinAndMapOne(
        'ili.unitPrice',
        'ili.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'REPORTED_PRICE\')'
      )
      .leftJoinAndMapOne(
        'ili.bestFitPrice',
        'ili.inventoryPrices',
        'bestFitInvPrice',
        '"bestFitInvPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'BEST_FIT_PRICE\')'
      )
      .leftJoinAndMapOne(
        'ili.invConvertedPrice',
        'ili.inventoryPrices',
        'invConvertedInvPrice',
        '"invConvertedInvPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'INV_CONVERTED\')'
      )
      .offset(offset)
      .limit(limit);
    // add filters for quantity filters
    let quantityFiltersMap = new Map();
    quantityFiltersMap.set('onOrderQuantity', 'onOrderInvQuantity');
    quantityFiltersMap.set('onHandQuantity', 'onHandInvQuantity');
    quantityFiltersMap.set('committedQuantity', 'committedInvQuantity');
    quantityFiltersMap.set('floatQuantity', 'floatInvQuantity');
    quantityFiltersMap.set('backorderedQuantity', 'backorderedInvQuantity');
    quantityFiltersMap.set('returnedQuantity', 'returnedInvQuantity');
    quantityFiltersMap.set('inTransitQuantity', 'inTransitInvQuantity');
    quantityFiltersMap.set('inStockQuantity', 'inStockInvQuantity');
    quantityFiltersMap.set('damagedQuantity', 'damagedInvQuantity');
    quantityFiltersMap.set('transferredQuantity', 'transferredInvQuantity');

    for (let [key, value] of quantityFiltersMap) {
      const quantityFilters = filters[key];
      delete filters[key];
      if (quantityFilters) {
        const quantityWhere =
          this.inventoryQuantityService.buildWhereExpression(
            quantityFilters,
            value
          );
        query.andWhere(quantityWhere);
      }
    }
    this.buildWhere(filters, query);
    query
      .andWhere('"ili"."INVENTORY_DATE" >= sysdate - ' + maxAge)
      .andWhere('"ili"."CUSTOMER_SID" = ' + customer.sid);
    // add order by
    this.addOrderBys(query, sort);
    if (!PartnerService.isAll(partnerId)) {
      query.andWhere('"df"."REPORTING_PARTNER_SID" = ' + partner.sid);
    }
    const inventory = await query.getMany();
    return this.loadIliValidationAggregation(inventory);
  }

  protected async loadIliValidationAggregation(
    inventory: Inventory[]
  ): Promise<Inventory[]> {
    if (inventory.length === 0) return inventory;

    // construct SQL
    const sql =
      ' select ' +
      ' ili.sid, count(iliv.inv_Line_item_sid) validationCodesCount ' +
      ' from inv_line_item ili ' +
      ' left join ili_validation iliv on iliv.inv_Line_item_sid =  ili.sid  ' +
      ' and iliv.customer_sid = ili.CUSTOMER_SID  ' +
      ' where ili.customer_sid = :customersid and ili.sid in (:iliSids)' +
      ' group by ili.sid ';

    // execute SQL
    var rows = new Map();
    for (var i = 0; i < inventory.length; i += 1000) {
      const iliSids = inventory.slice(i, i + 1000).map((s) => s.sid);
      //not a param to avoid bind var peaking
      var inListSql = sql.replace(
        ':customersid',
        inventory[0].customerSid.toString() as string
      );
      inListSql = inListSql.replace(':iliSids', iliSids.join(','));

      var results = await this.repository.query(inListSql);
      results.map((r) => {
        rows.set(r.SID, r.VALIDATIONCODESCOUNT);
      });
    }
    // map SQL result
    return inventory.map((inventory) => {
      inventory.validationCodesCount = rows.get(inventory.sid);
      return inventory;
    });
  }

  async findNeedCorrectionInventory(
    customerId: string,
    partnerId: string,
    options: FindOptions = new FindOptions()
  ): Promise<Inventory[]> {
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    const { offset, limit, filters = {}, sort } = options;

    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return Promise.resolve([]);
    }
    const config = await this.configurationService.getConfiguration(customerId);
    const queueName = config.get('needCorrectionInventoryQueueName', 'resign');
    const maxAge = config.get('nmiMaxAgeInDays', '30');

    const query = this.repository
      .createQueryBuilder('inv')
      .leftJoinAndSelect(
        'inv.dynamicAttrs',
        'da',
        '"da"."ATTRIBUTE_TYPE" = \'IL\' and "da"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .innerJoinAndSelect(
        'inv.dataFile',
        'df',
        '"df"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect(
        'inv.submissionPeriod',
        'sp',
        '"sp"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect(
        'sp.submissionSchedule',
        'ss',
        '"sp"."CUSTOMER_SID" = "ss"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect('ss.dataType', 'dt')
      .leftJoinAndSelect(
        'sp.submissionPeriodLineItemView',
        'spli',
        '"sp"."CUSTOMER_SID" = "spli"."CUSTOMER_SID"'
      )
      .leftJoinAndMapOne(
        'inv.onHandQuantity',
        'inv.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'inv.onOrderQuantity',
        'inv.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'inv.committedQuantity',
        'inv.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'inv.floatQuantity',
        'inv.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'inv.backorderedQuantity',
        'inv.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'inv.returnedQuantity',
        'inv.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'inv.inTransitQuantity',
        'inv.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'inv.unitPrice',
        'inv.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
      )
      .innerJoin(
        'PEH_INV_QUEUE',
        'piq',
        '"piq"."CLIENT_SKU" = "inv"."CLIENT_SKU" and "piq"."INV_LINE_ITEM_SID" = "inv"."SID" ' +
          'and "piq"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .offset(offset)
      .limit(limit);

    // add filters for quantity filters
    let quantityFiltersMap = new Map();
    quantityFiltersMap.set('onOrderQuantity', 'onOrderInvQuantity');
    quantityFiltersMap.set('onHandQuantity', 'onHandInvQuantity');
    quantityFiltersMap.set('committedQuantity', 'committedInvQuantity');
    quantityFiltersMap.set('floatQuantity', 'floatInvQuantity');
    quantityFiltersMap.set('backorderedQuantity', 'backorderedInvQuantity');
    quantityFiltersMap.set('returnedQuantity', 'returnedInvQuantity');
    quantityFiltersMap.set('inTransitQuantity', 'inTransitInvQuantity');

    for (let [key, value] of quantityFiltersMap) {
      const quantityFilters = filters[key];
      delete filters[key];
      if (quantityFilters) {
        const quantityWhere =
          this.inventoryQuantityService.buildWhereExpression(
            quantityFilters,
            value
          );
        query.andWhere(quantityWhere);
      }
    }

    // add unit price condition
    const unitPriceFilters = filters['unitPrice'];
    delete filters['unitPrice'];
    if (unitPriceFilters) {
      const unitPriceWhere = this.inventoryPriceService.buildWhereExpression(
        unitPriceFilters,
        'invPrice'
      );
      query.andWhere(unitPriceWhere);
    }

    this.buildWhere(filters, query);
    query
      .andWhere('"inv"."CUSTOMER_SID" = ' + customer.sid)
      .andWhere('"inv"."INVENTORY_DATE" >= sysdate - ' + maxAge)
      .andWhere('"piq"."QUEUE_NAME" = \'' + queueName + "'")
      .andWhere({ deleted: Equal(0) });

    if (!PartnerService.isAll(partnerId)) {
      query.andWhere('"inv"."REPORTING_PARTNER_SID" = ' + partner.sid);
    }

    // add order by
    this.addOrderBys(query, sort);

    return query.getMany();
  }

  async resubmitInventory(
    customerId: string,
    partnerId: string,
    inputs: Partial<InventoryResubmitInput>[]
  ): Promise<ServiceError[]> {
    // Check input array size
    if (inputs.length === 0) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          'No inventory items provided for resubmission.'
        )
      ];
    }
    //Inventory Item Limit Check
    if (inputs.length > 1000) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          `The number of inventory items exceeds the limit of 1000.`
        )
      ];
    }
    const sids: number[] = inputs.map((input: InventoryInput) => {
      return input.sid;
    });
    let errors: ServiceError[] = [];

    // Check if partner has access to inventory items
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          `Customer ${customerId} or partner ${partnerId} are invalid.`
        )
      ];
    }

    // Query to fetch inventory items with required joins

    let inventoryItems: Inventory[] = await this.repository
      .createQueryBuilder('inv')
      .innerJoinAndSelect('inv.reportingPartner', 'rp')
      .leftJoinAndSelect('rp.gsNumbers', 'gs')
      .leftJoinAndSelect('inv.dynamicAttrs', 'da')
      .leftJoinAndMapOne(
        'inv.onHandQuantity',
        'inv.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'inv.onOrderQuantity',
        'inv.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'inv.committedQuantity',
        'inv.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'inv.floatQuantity',
        'inv.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'inv.backorderedQuantity',
        'inv.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'inv.returnedQuantity',
        'inv.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'inv.inTransitQuantity',
        'inv.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'inv.unitPrice',
        'inv.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = (SELECT SID FROM PRICE_TYPE WHERE NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
      )
      .where({ sid: In(sids) })
      .andWhere('"gs"."CUSTOMER_SID" = :customerSid', {
        customerSid: customer.sid
      })
      .getMany();

    let inventoryItemMap = new Map(
      inventoryItems.map((inventoryItem) => [
        Number(inventoryItem.sid),
        inventoryItem
      ])
    );

    let inventoryItemsToResubmit = [];
    errors = await Promise.all(
      inputs.map(async (inventoryResubmitInput) => {
        const inventoryItem = inventoryItemMap.get(
          Number(inventoryResubmitInput.sid)
        );
        if (inventoryItem) {
          let resubmitInventoryItem =
            await this.loadResubmitInventoryLineItemRefs(
              inventoryItem,
              inventoryResubmitInput
            );
          inventoryItemsToResubmit.push(resubmitInventoryItem);
          return null;
        } else {
          return new ServiceError(
            'INVENTORY_ITEM_ERR',
            `Inventory Item with IID ${inventoryResubmitInput.sid} not found. Please check the input data.`
          );
        }
      })
    );

    console.log(sids)
    let partnerIdResult = await this.repository
    .createQueryBuilder('ili')
    .innerJoin('ili.reportingPartner','rp')
    .innerJoin('ili.customer', 'c')
    .where('ili.sid = :sids' , {sids : sids[0]})
    .andWhere('ili.CUSTOMER_SID = :customerSid', {
      customerSid: customer.sid
    }).select('rp.id').getRawOne();
 
    let reportingPartnerId: string = '';
    if (partnerIdResult) {
      reportingPartnerId = String(partnerIdResult['rp_ID']);
    } else {
        console.log('No reporting partner ID found');
              errors.push(new ServiceError(
        'INVENTORY_ITEM_ERR',
        `Inventory Item with IID ${sids[0]} not found. Please check the input data.`
      ));
    }
    
    // Write to S3 or perform further operations with resubmitted inventory items
    console.log(
      `Inventory items to resubmit: ${inventoryItemsToResubmit.length}`
    );
    if (inventoryItemsToResubmit.length > 0) {
      // Example: Write to a service or perform operations with resubmitted inventory items
      await this.writer.write2(
        customerId,
        reportingPartnerId,
        INVENTORY_RESUBMIT,
        inventoryItemsToResubmit
      );
    }

    return errors;
  }

  clone(...objs) {
    return Object.assign({}, ...objs);
  }

  async loadResubmitInventoryLineItemRefs(
    inventoryItem: Inventory,
    inventoryResubmitInput: Partial<InventoryResubmitInput>
  ): Promise<Inventory> {
    let resubmitInventoryLineItem: Inventory = this.clone(
      inventoryItem,
      inventoryResubmitInput
    );

    resubmitInventoryLineItem.dynamicAttrs = this.clone(
      await inventoryItem.dynamicAttrs,
      inventoryResubmitInput.dynamicAttrs
    );

    resubmitInventoryLineItem.onHandQuantity = this.clone(
      inventoryItem.onHandQuantity,
      inventoryResubmitInput.onHandQuantity
    );

    resubmitInventoryLineItem.onOrderQuantity = this.clone(
      inventoryItem.onOrderQuantity,
      inventoryResubmitInput.onOrderQuantity
    );

    resubmitInventoryLineItem.committedQuantity = this.clone(
      inventoryItem.committedQuantity,
      inventoryResubmitInput.committedQuantity
    );

    resubmitInventoryLineItem.backorderedQuantity = this.clone(
      inventoryItem.backorderedQuantity,
      inventoryResubmitInput.backorderedQuantity
    );

    resubmitInventoryLineItem.returnedQuantity = this.clone(
      inventoryItem.returnedQuantity,
      inventoryResubmitInput.returnedQuantity
    );

    resubmitInventoryLineItem.inTransitQuantity = this.clone(
      inventoryItem.inTransitQuantity,
      inventoryResubmitInput.inTransitQuantity
    );

    resubmitInventoryLineItem.inStockQuantity = this.clone(
      inventoryItem.inStockQuantity,
      inventoryResubmitInput.inStockQuantity
    );

    resubmitInventoryLineItem.damagedQuantity = this.clone(
      inventoryItem.damagedQuantity,
      inventoryResubmitInput.damagedQuantity
    );

    resubmitInventoryLineItem.transferredQuantity = this.clone(
      inventoryItem.transferredQuantity,
      inventoryResubmitInput.transferredQuantity
    );

    resubmitInventoryLineItem.inventoryPrices = this.clone(
      await inventoryItem.inventoryPrices
    );

    resubmitInventoryLineItem.reportedPrice = this.clone(
      inventoryItem.reportedPrice,
      inventoryResubmitInput.reportedPrice
    );

    resubmitInventoryLineItem.reportingPartner = this.clone(
      await inventoryItem.reportingPartner
    );

    return resubmitInventoryLineItem;
  }

  async updateInventory(
    customerId: string,
    partnerId: string,
    data: InventoryInput[]
  ): Promise<ServiceError[]> {
    let inventoryToCreate: Inventory[] = [];
    let sids: number[] = data.map((input: InventoryInput) => {
      return input.sid;
    });
    let errors: ServiceError[] = [];

    // Check if partner has access to ili
    let options: FindOptions = {
      offset: 0,
      limit: 1000,
      filters: {
        sid: {
          operator: Operator.IN,
          values: sids
        }
      }
    };
    let inventory: Inventory[] = await this.findNeedCorrectionInventory(
      customerId,
      partnerId,
      options
    );

    let sidsToUpdate: number[] = [];
    errors = sids.map((sid) => {
      if (inventory) {
        let s: Inventory = inventory.find((inv: Inventory) => {
          return inv.sid.toString() === sid.toString();
        });
        if (s) {
          sidsToUpdate.push(sid);
          return null;
        }
      }
      return new ServiceError(
        'INVENTORY_NOT_FOUND',
        `Inventory line sid : ${sid} not found`
      );
    });

    if (inventory && inventory.length > 0) {
      let invLineItems: Inventory[] = await this.repository
        .createQueryBuilder('inv')
        // load partner and gs number
        .innerJoinAndSelect('inv.reportingPartner', 'rp')
        .leftJoinAndSelect('rp.gsNumbers', 'gs')
        // load dynamic attrs
        .leftJoinAndSelect('inv.dynamicAttrs', 'da')
        .leftJoinAndMapOne(
          'inv.onHandQuantity',
          'inv.inventoryQuantities',
          'onHandInvQuantity',
          '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
        )
        .leftJoinAndMapOne(
          'inv.onOrderQuantity',
          'inv.inventoryQuantities',
          'onOrderInvQuantity',
          '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
        )
        .leftJoinAndMapOne(
          'inv.committedQuantity',
          'inv.inventoryQuantities',
          'committedInvQuantity',
          '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
        )
        .leftJoinAndMapOne(
          'inv.floatQuantity',
          'inv.inventoryQuantities',
          'floatInvQuantity',
          '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
        )
        .leftJoinAndMapOne(
          'inv.backorderedQuantity',
          'inv.inventoryQuantities',
          'backorderedInvQuantity',
          '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
        )
        .leftJoinAndMapOne(
          'inv.returnedQuantity',
          'inv.inventoryQuantities',
          'returnedInvQuantity',
          '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
        )
        .leftJoinAndMapOne(
          'inv.inTransitQuantity',
          'inv.inventoryQuantities',
          'inTransitInvQuantity',
          '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
        )
        .leftJoinAndMapOne(
          'inv.unitPrice',
          'inv.inventoryPrices',
          'invPrice',
          '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
        )
        .where({ sid: In(sidsToUpdate) })
        .andWhere('"gs"."CUSTOMER_SID" = :customerSid', {
          customerSid: inventory[0].customerSid
        })
        .getMany();

      await Promise.all(
        invLineItems.map(async (invLineItem) => {
          // load serial numbers

          let input: InventoryInput = data.find((inv: InventoryInput) => {
            return inv.sid.toString() === invLineItem.sid.toString();
          });

          invLineItem = Object.assign({}, invLineItem, input);

          // add inv line to list to write to S3.
          inventoryToCreate.push(invLineItem);
        })
      );

      console.log(`Inventory to write to S3 : ${inventoryToCreate.length}`);
      // Write to S3
      if (inventoryToCreate.length > 0) {
        await this.writer.write(
          customerId,
          partnerId,
          this.getServiceName(),
          inventoryToCreate
        );
      }

      // It is safe to delete from PEH after writing to S3, just in case if delete fails,
      // the inv will still be deleted from PEH the json file from S3 is loaded into nucleus.
      // If we delete first and for some reason the writing to S3 fails, there is no way to roll back the db update.
      await Promise.all(
        inventoryToCreate.map(async (invLineItem) => {
          try {
            // delete inventory line from PEH
            await AppDataSource.query(
              'delete from peh_inv_queue where inv_line_item_sid = :sid',
              [invLineItem.sid]
            );
            console.log(`Deleted line item : ${invLineItem.sid} from PEH`);
          } catch (e) {
            console.log(
              `Error while deleting from PEH for inv line : ${JSON.stringify(
                invLineItem
              )} for Customer : ${customerId}`
            );
            console.error(e);
            // eat the error as if delete fails, the only con is User will still be able to see the inv line
            // until the file on S3 is loaded into nucleus
          }
        })
      );
    }

    return errors;
  }
}
import { DomainEntityService } from '../base/DomainEntityService';
import { Inventory } from './Inventory';
import { Service, Inject } from 'typedi';
import { FindOptions } from '../base/FindOptions';
import { Equal, In } from 'typeorm';
import { ConfigurationService } from '../configuration/ConfigurationService';
import { CustomerService } from '../customer/CustomerService';
import { PartnerService } from '../partner/PartnerService';
import { DynamicAttrsService } from '../attribute/DynamicAttrsService';
import { DataFileService } from '../datafile/DataFileService';
import { SubmissionPeriodService } from '../submission/SubmissionPeriodService';
import { InventoryQuantityService } from './InventoryQuantityService';
import { InventoryPriceService } from './InventoryPriceService';
import { Writer } from '../../writer/Writer';
import { InventoryInput } from './InventoryInput';
import { ServiceError } from '../base/ServiceError';
import { Operator } from '../base/filters/Operator';
import { AppDataSource } from '../../platform/DataSource';
import { InventoryResubmitInput } from './InventoryResubmitInput';

const INVENTORY_RESUBMIT = 'InventoryResubmit';

@Service()
export class InventoryService extends DomainEntityService<Inventory> {
  @Inject()
  protected configurationService: ConfigurationService;

  @Inject()
  protected customerService: CustomerService;

  @Inject()
  protected partnerService: PartnerService;

  @Inject()
  protected dynamicAttrsService: DynamicAttrsService;

  @Inject()
  protected dataFileService: DataFileService;

  @Inject()
  protected submissionPeriodService: SubmissionPeriodService;

  @Inject()
  protected inventoryQuantityService: InventoryQuantityService;

  @Inject()
  protected inventoryPriceService: InventoryPriceService;

  @Inject('Writer')
  protected writer: Writer;

  constructor() {
    super(Inventory);
  }

  getServiceName(): string {
    return 'Inventory';
  }

  async findInventory(
    customerId: string,
    partnerId: string,
    options: FindOptions = new FindOptions()
  ): Promise<Inventory[]> {
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    const config = await this.configurationService.getConfiguration(customerId);
    const { offset, limit, filters = {}, sort } = options;

    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return Promise.resolve([]);
    }
    const maxAge = config.get('nmiMaxAgeInDays', '30');
    const query = this.repository
      .createQueryBuilder('ili')
      .leftJoinAndSelect(
        'ili.dynamicAttrs',
        'da',
        '"da"."ATTRIBUTE_TYPE" = \'IL\' and "da"."CUSTOMER_SID" = "ili"."CUSTOMER_SID"'
      )
      .innerJoinAndSelect(
        'ili.dataFile',
        'df',
        '"df"."CUSTOMER_SID" = "ili"."CUSTOMER_SID"'
      )
      .leftJoinAndMapOne(
        'ili.onHandQuantity',
        'ili.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'ili.onOrderQuantity',
        'ili.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'ili.committedQuantity',
        'ili.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'ili.floatQuantity',
        'ili.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'ili.backorderedQuantity',
        'ili.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'ili.returnedQuantity',
        'ili.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'ili.inTransitQuantity',
        'ili.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'ili.inStockQuantity',
        'ili.inventoryQuantities',
        'inStockInvQuantity',
        '"inStockInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'IN STOCK\')'
      )
      .leftJoinAndMapOne(
        'ili.damagedQuantity',
        'ili.inventoryQuantities',
        'damagedInvQuantity',
        '"damagedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'DAMAGED\')'
      )
      .leftJoinAndMapOne(
        'ili.transferredQuantity',
        'ili.inventoryQuantities',
        'transferredInvQuantity',
        '"transferredInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSFERRED\')'
      )
      .leftJoinAndMapOne(
        'ili.unitPrice',
        'ili.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'REPORTED_PRICE\')'
      )
      .leftJoinAndMapOne(
        'ili.bestFitPrice',
        'ili.inventoryPrices',
        'bestFitInvPrice',
        '"bestFitInvPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'BEST_FIT_PRICE\')'
      )
      .leftJoinAndMapOne(
        'ili.invConvertedPrice',
        'ili.inventoryPrices',
        'invConvertedInvPrice',
        '"invConvertedInvPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'INV_CONVERTED\')'
      )
      .offset(offset)
      .limit(limit);
    // add filters for quantity filters
    let quantityFiltersMap = new Map();
    quantityFiltersMap.set('onOrderQuantity', 'onOrderInvQuantity');
    quantityFiltersMap.set('onHandQuantity', 'onHandInvQuantity');
    quantityFiltersMap.set('committedQuantity', 'committedInvQuantity');
    quantityFiltersMap.set('floatQuantity', 'floatInvQuantity');
    quantityFiltersMap.set('backorderedQuantity', 'backorderedInvQuantity');
    quantityFiltersMap.set('returnedQuantity', 'returnedInvQuantity');
    quantityFiltersMap.set('inTransitQuantity', 'inTransitInvQuantity');
    quantityFiltersMap.set('inStockQuantity', 'inStockInvQuantity');
    quantityFiltersMap.set('damagedQuantity', 'damagedInvQuantity');
    quantityFiltersMap.set('transferredQuantity', 'transferredInvQuantity');

    for (let [key, value] of quantityFiltersMap) {
      const quantityFilters = filters[key];
      delete filters[key];
      if (quantityFilters) {
        const quantityWhere =
          this.inventoryQuantityService.buildWhereExpression(
            quantityFilters,
            value
          );
        query.andWhere(quantityWhere);
      }
    }
    this.buildWhere(filters, query);
    query
      .andWhere('"ili"."INVENTORY_DATE" >= sysdate - ' + maxAge)
      .andWhere('"ili"."CUSTOMER_SID" = ' + customer.sid);
    // add order by
    this.addOrderBys(query, sort);
    if (!PartnerService.isAll(partnerId)) {
      query.andWhere('"df"."REPORTING_PARTNER_SID" = ' + partner.sid);
    }
    const inventory = await query.getMany();
    return this.loadIliValidationAggregation(inventory);
  }

  protected async loadIliValidationAggregation(
    inventory: Inventory[]
  ): Promise<Inventory[]> {
    if (inventory.length === 0) return inventory;

    // construct SQL
    const sql =
      ' select ' +
      ' ili.sid, count(iliv.inv_Line_item_sid) validationCodesCount ' +
      ' from inv_line_item ili ' +
      ' left join ili_validation iliv on iliv.inv_Line_item_sid =  ili.sid  ' +
      ' and iliv.customer_sid = ili.CUSTOMER_SID  ' +
      ' where ili.customer_sid = :customersid and ili.sid in (:iliSids)' +
      ' group by ili.sid ';

    // execute SQL
    var rows = new Map();
    for (var i = 0; i < inventory.length; i += 1000) {
      const iliSids = inventory.slice(i, i + 1000).map((s) => s.sid);
      //not a param to avoid bind var peaking
      var inListSql = sql.replace(
        ':customersid',
        inventory[0].customerSid.toString() as string
      );
      inListSql = inListSql.replace(':iliSids', iliSids.join(','));

      var results = await this.repository.query(inListSql);
      results.map((r) => {
        rows.set(r.SID, r.VALIDATIONCODESCOUNT);
      });
    }
    // map SQL result
    return inventory.map((inventory) => {
      inventory.validationCodesCount = rows.get(inventory.sid);
      return inventory;
    });
  }

  async findNeedCorrectionInventory(
    customerId: string,
    partnerId: string,
    options: FindOptions = new FindOptions()
  ): Promise<Inventory[]> {
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    const { offset, limit, filters = {}, sort } = options;

    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return Promise.resolve([]);
    }
    const config = await this.configurationService.getConfiguration(customerId);
    const queueName = config.get('needCorrectionInventoryQueueName', 'resign');
    const maxAge = config.get('nmiMaxAgeInDays', '30');

    const query = this.repository
      .createQueryBuilder('inv')
      .leftJoinAndSelect(
        'inv.dynamicAttrs',
        'da',
        '"da"."ATTRIBUTE_TYPE" = \'IL\' and "da"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .innerJoinAndSelect(
        'inv.dataFile',
        'df',
        '"df"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect(
        'inv.submissionPeriod',
        'sp',
        '"sp"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect(
        'sp.submissionSchedule',
        'ss',
        '"sp"."CUSTOMER_SID" = "ss"."CUSTOMER_SID"'
      )
      .leftJoinAndSelect('ss.dataType', 'dt')
      .leftJoinAndSelect(
        'sp.submissionPeriodLineItemView',
        'spli',
        '"sp"."CUSTOMER_SID" = "spli"."CUSTOMER_SID"'
      )
      .leftJoinAndMapOne(
        'inv.onHandQuantity',
        'inv.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'inv.onOrderQuantity',
        'inv.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'inv.committedQuantity',
        'inv.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'inv.floatQuantity',
        'inv.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'inv.backorderedQuantity',
        'inv.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'inv.returnedQuantity',
        'inv.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'inv.inTransitQuantity',
        'inv.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'inv.unitPrice',
        'inv.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE ' +
          'NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
      )
      .innerJoin(
        'PEH_INV_QUEUE',
        'piq',
        '"piq"."CLIENT_SKU" = "inv"."CLIENT_SKU" and "piq"."INV_LINE_ITEM_SID" = "inv"."SID" ' +
          'and "piq"."CUSTOMER_SID" = "inv"."CUSTOMER_SID"'
      )
      .offset(offset)
      .limit(limit);

    // add filters for quantity filters
    let quantityFiltersMap = new Map();
    quantityFiltersMap.set('onOrderQuantity', 'onOrderInvQuantity');
    quantityFiltersMap.set('onHandQuantity', 'onHandInvQuantity');
    quantityFiltersMap.set('committedQuantity', 'committedInvQuantity');
    quantityFiltersMap.set('floatQuantity', 'floatInvQuantity');
    quantityFiltersMap.set('backorderedQuantity', 'backorderedInvQuantity');
    quantityFiltersMap.set('returnedQuantity', 'returnedInvQuantity');
    quantityFiltersMap.set('inTransitQuantity', 'inTransitInvQuantity');

    for (let [key, value] of quantityFiltersMap) {
      const quantityFilters = filters[key];
      delete filters[key];
      if (quantityFilters) {
        const quantityWhere =
          this.inventoryQuantityService.buildWhereExpression(
            quantityFilters,
            value
          );
        query.andWhere(quantityWhere);
      }
    }

    // add unit price condition
    const unitPriceFilters = filters['unitPrice'];
    delete filters['unitPrice'];
    if (unitPriceFilters) {
      const unitPriceWhere = this.inventoryPriceService.buildWhereExpression(
        unitPriceFilters,
        'invPrice'
      );
      query.andWhere(unitPriceWhere);
    }

    this.buildWhere(filters, query);
    query
      .andWhere('"inv"."CUSTOMER_SID" = ' + customer.sid)
      .andWhere('"inv"."INVENTORY_DATE" >= sysdate - ' + maxAge)
      .andWhere('"piq"."QUEUE_NAME" = \'' + queueName + "'")
      .andWhere({ deleted: Equal(0) });

    if (!PartnerService.isAll(partnerId)) {
      query.andWhere('"inv"."REPORTING_PARTNER_SID" = ' + partner.sid);
    }

    // add order by
    this.addOrderBys(query, sort);

    return query.getMany();
  }

  async resubmitInventory(
    customerId: string,
    partnerId: string,
    inputs: Partial<InventoryResubmitInput>[]
  ): Promise<ServiceError[]> {
    // Check input array size
    if (inputs.length === 0) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          'No inventory items provided for resubmission.'
        )
      ];
    }
    //Inventory Item Limit Check
    if (inputs.length > 1000) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          `The number of inventory items exceeds the limit of 1000.`
        )
      ];
    }
    const sids: number[] = inputs.map((input: InventoryInput) => {
      return input.sid;
    });
    let errors: ServiceError[] = [];

    // Check if partner has access to inventory items
    const customer = await this.customerService.findOneById(customerId);
    const partner = await this.partnerService.findOneById(partnerId);
    if (!customer || (!partner && !PartnerService.isAll(partnerId))) {
      return [
        new ServiceError(
          'INVENTORY_ITEM_ERR',
          `Customer ${customerId} or partner ${partnerId} are invalid.`
        )
      ];
    }

    // Query to fetch inventory items with required joins

    let inventoryItems: Inventory[] = await this.repository
      .createQueryBuilder('inv')
      .innerJoinAndSelect('inv.reportingPartner', 'rp')
      .leftJoinAndSelect('rp.gsNumbers', 'gs')
      .leftJoinAndSelect('inv.dynamicAttrs', 'da')
      .leftJoinAndMapOne(
        'inv.onHandQuantity',
        'inv.inventoryQuantities',
        'onHandInvQuantity',
        '"onHandInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
      )
      .leftJoinAndMapOne(
        'inv.onOrderQuantity',
        'inv.inventoryQuantities',
        'onOrderInvQuantity',
        '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
      )
      .leftJoinAndMapOne(
        'inv.committedQuantity',
        'inv.inventoryQuantities',
        'committedInvQuantity',
        '"committedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
      )
      .leftJoinAndMapOne(
        'inv.floatQuantity',
        'inv.inventoryQuantities',
        'floatInvQuantity',
        '"floatInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
      )
      .leftJoinAndMapOne(
        'inv.backorderedQuantity',
        'inv.inventoryQuantities',
        'backorderedInvQuantity',
        '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
      )
      .leftJoinAndMapOne(
        'inv.returnedQuantity',
        'inv.inventoryQuantities',
        'returnedInvQuantity',
        '"returnedInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
      )
      .leftJoinAndMapOne(
        'inv.inTransitQuantity',
        'inv.inventoryQuantities',
        'inTransitInvQuantity',
        '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = (SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
      )
      .leftJoinAndMapOne(
        'inv.unitPrice',
        'inv.inventoryPrices',
        'invPrice',
        '"invPrice"."PRICE_TYPE_SID" = (SELECT SID FROM PRICE_TYPE WHERE NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
      )
      .where({ sid: In(sids) })
      .andWhere('"gs"."CUSTOMER_SID" = :customerSid', {
        customerSid: customer.sid
      })
      .getMany();

    let inventoryItemMap = new Map(
      inventoryItems.map((inventoryItem) => [
        Number(inventoryItem.sid),
        inventoryItem
      ])
    );

    let inventoryItemsToResubmit = [];
    errors = await Promise.all(
      inputs.map(async (inventoryResubmitInput) => {
        const inventoryItem = inventoryItemMap.get(
          Number(inventoryResubmitInput.sid)
        );
        if (inventoryItem) {
          let resubmitInventoryItem =
            await this.loadResubmitInventoryLineItemRefs(
              inventoryItem,
              inventoryResubmitInput
            );
          inventoryItemsToResubmit.push(resubmitInventoryItem);
          return null;
        } else {
          return new ServiceError(
            'INVENTORY_ITEM_ERR',
            `Inventory Item with IID ${inventoryResubmitInput.sid} not found. Please check the input data.`
          );
        }
      })
    );

    let partnerIdResult = await this.repository
    .createQueryBuilder('ili')
    .innerJoin('ili.reportingPartner','rp')
    .innerJoin('ili.customer', 'c')
    .where('ili.sid = :sids' , {sids : sids[0]})
    .andWhere('ili.CUSTOMER_SID = :customerSid', {
      customerSid: customer.sid
    }).select('rp.id').getRawOne();
 
    let rpid: string = '';
if (partnerIdResult) {
    rpid = String(partnerIdResult['rp_ID']); // Convert the ID to a string
    console.log('rpid as string:', rpid); // Log the string value
} else {
    console.log('No reporting partner ID found');
}
    
    // Write to S3 or perform further operations with resubmitted inventory items
    console.log(
      `Inventory items to resubmit: ${inventoryItemsToResubmit.length}`
    );
    if (inventoryItemsToResubmit.length > 0) {
      // Example: Write to a service or perform operations with resubmitted inventory items
      await this.writer.write2(
        customerId,
        rpid,
        INVENTORY_RESUBMIT,
        inventoryItemsToResubmit
      );
    }

    return errors;
  }

  clone(...objs) {
    return Object.assign({}, ...objs);
  }

  async loadResubmitInventoryLineItemRefs(
    inventoryItem: Inventory,
    inventoryResubmitInput: Partial<InventoryResubmitInput>
  ): Promise<Inventory> {
    let resubmitInventoryLineItem: Inventory = this.clone(
      inventoryItem,
      inventoryResubmitInput
    );

    resubmitInventoryLineItem.dynamicAttrs = this.clone(
      await inventoryItem.dynamicAttrs,
      inventoryResubmitInput.dynamicAttrs
    );

    resubmitInventoryLineItem.onHandQuantity = this.clone(
      inventoryItem.onHandQuantity,
      inventoryResubmitInput.onHandQuantity
    );

    resubmitInventoryLineItem.onOrderQuantity = this.clone(
      inventoryItem.onOrderQuantity,
      inventoryResubmitInput.onOrderQuantity
    );

    resubmitInventoryLineItem.committedQuantity = this.clone(
      inventoryItem.committedQuantity,
      inventoryResubmitInput.committedQuantity
    );

    resubmitInventoryLineItem.backorderedQuantity = this.clone(
      inventoryItem.backorderedQuantity,
      inventoryResubmitInput.backorderedQuantity
    );

    resubmitInventoryLineItem.returnedQuantity = this.clone(
      inventoryItem.returnedQuantity,
      inventoryResubmitInput.returnedQuantity
    );

    resubmitInventoryLineItem.inTransitQuantity = this.clone(
      inventoryItem.inTransitQuantity,
      inventoryResubmitInput.inTransitQuantity
    );

    resubmitInventoryLineItem.inStockQuantity = this.clone(
      inventoryItem.inStockQuantity,
      inventoryResubmitInput.inStockQuantity
    );

    resubmitInventoryLineItem.damagedQuantity = this.clone(
      inventoryItem.damagedQuantity,
      inventoryResubmitInput.damagedQuantity
    );

    resubmitInventoryLineItem.transferredQuantity = this.clone(
      inventoryItem.transferredQuantity,
      inventoryResubmitInput.transferredQuantity
    );

    resubmitInventoryLineItem.inventoryPrices = this.clone(
      await inventoryItem.inventoryPrices
    );

    resubmitInventoryLineItem.reportedPrice = this.clone(
      inventoryItem.reportedPrice,
      inventoryResubmitInput.reportedPrice
    );

    resubmitInventoryLineItem.reportingPartner = this.clone(
      await inventoryItem.reportingPartner
    );

    return resubmitInventoryLineItem;
  }

  async updateInventory(
    customerId: string,
    partnerId: string,
    data: InventoryInput[]
  ): Promise<ServiceError[]> {
    let inventoryToCreate: Inventory[] = [];
    let sids: number[] = data.map((input: InventoryInput) => {
      return input.sid;
    });
    let errors: ServiceError[] = [];

    // Check if partner has access to ili
    let options: FindOptions = {
      offset: 0,
      limit: 1000,
      filters: {
        sid: {
          operator: Operator.IN,
          values: sids
        }
      }
    };
    let inventory: Inventory[] = await this.findNeedCorrectionInventory(
      customerId,
      partnerId,
      options
    );

    let sidsToUpdate: number[] = [];
    errors = sids.map((sid) => {
      if (inventory) {
        let s: Inventory = inventory.find((inv: Inventory) => {
          return inv.sid.toString() === sid.toString();
        });
        if (s) {
          sidsToUpdate.push(sid);
          return null;
        }
      }
      return new ServiceError(
        'INVENTORY_NOT_FOUND',
        `Inventory line sid : ${sid} not found`
      );
    });

    if (inventory && inventory.length > 0) {
      let invLineItems: Inventory[] = await this.repository
        .createQueryBuilder('inv')
        // load partner and gs number
        .innerJoinAndSelect('inv.reportingPartner', 'rp')
        .leftJoinAndSelect('rp.gsNumbers', 'gs')
        // load dynamic attrs
        .leftJoinAndSelect('inv.dynamicAttrs', 'da')
        .leftJoinAndMapOne(
          'inv.onHandQuantity',
          'inv.inventoryQuantities',
          'onHandInvQuantity',
          '"onHandInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON HAND\')'
        )
        .leftJoinAndMapOne(
          'inv.onOrderQuantity',
          'inv.inventoryQuantities',
          'onOrderInvQuantity',
          '"onOrderInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'ON ORDER\')'
        )
        .leftJoinAndMapOne(
          'inv.committedQuantity',
          'inv.inventoryQuantities',
          'committedInvQuantity',
          '"committedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'COMMITTED\')'
        )
        .leftJoinAndMapOne(
          'inv.floatQuantity',
          'inv.inventoryQuantities',
          'floatInvQuantity',
          '"floatInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'FLOAT\')'
        )
        .leftJoinAndMapOne(
          'inv.backorderedQuantity',
          'inv.inventoryQuantities',
          'backorderedInvQuantity',
          '"backorderedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'BACKORDERED\')'
        )
        .leftJoinAndMapOne(
          'inv.returnedQuantity',
          'inv.inventoryQuantities',
          'returnedInvQuantity',
          '"returnedInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'RETURNED\')'
        )
        .leftJoinAndMapOne(
          'inv.inTransitQuantity',
          'inv.inventoryQuantities',
          'inTransitInvQuantity',
          '"inTransitInvQuantity"."QUANTITY_TYPE_SID" = ( SELECT SID FROM QUANTITY_TYPE WHERE QUANTITY_TYPE = \'TRANSIT\')'
        )
        .leftJoinAndMapOne(
          'inv.unitPrice',
          'inv.inventoryPrices',
          'invPrice',
          '"invPrice"."PRICE_TYPE_SID" = ( SELECT SID FROM PRICE_TYPE WHERE NAME = \'REPORTED_PRICE\') AND "invPrice"."DELETED" = 0'
        )
        .where({ sid: In(sidsToUpdate) })
        .andWhere('"gs"."CUSTOMER_SID" = :customerSid', {
          customerSid: inventory[0].customerSid
        })
        .getMany();

      await Promise.all(
        invLineItems.map(async (invLineItem) => {
          // load serial numbers

          let input: InventoryInput = data.find((inv: InventoryInput) => {
            return inv.sid.toString() === invLineItem.sid.toString();
          });

          invLineItem = Object.assign({}, invLineItem, input);

          // add inv line to list to write to S3.
          inventoryToCreate.push(invLineItem);
        })
      );

      console.log(`Inventory to write to S3 : ${inventoryToCreate.length}`);
      // Write to S3
      if (inventoryToCreate.length > 0) {
        await this.writer.write(
          customerId,
          partnerId,
          this.getServiceName(),
          inventoryToCreate
        );
      }

      // It is safe to delete from PEH after writing to S3, just in case if delete fails,
      // the inv will still be deleted from PEH the json file from S3 is loaded into nucleus.
      // If we delete first and for some reason the writing to S3 fails, there is no way to roll back the db update.
      await Promise.all(
        inventoryToCreate.map(async (invLineItem) => {
          try {
            // delete inventory line from PEH
            await AppDataSource.query(
              'delete from peh_inv_queue where inv_line_item_sid = :sid',
              [invLineItem.sid]
            );
            console.log(`Deleted line item : ${invLineItem.sid} from PEH`);
          } catch (e) {
            console.log(
              `Error while deleting from PEH for inv line : ${JSON.stringify(
                invLineItem
              )} for Customer : ${customerId}`
            );
            console.error(e);
            // eat the error as if delete fails, the only con is User will still be able to see the inv line
            // until the file on S3 is loaded into nucleus
          }
        })
      );
    }

    return errors;
  }
}
package com.modeln.cdm.service.translation;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.infonow.service.recovery.RecoveryQueries;
import org.json.JSONException;
import org.json.JSONObject;
import com.infonow.service.recovery.TranslationOrphanStatusEnum;
import com.infonow.crux.dao.InowProfileDao;
import com.infonow.crux.impl.InowProfileImpl;
import com.infonow.framework.service.ServiceResponse;
import com.infonow.framework.service.configuration.support.ServiceDefinition;
import com.infonow.framework.service.context.ServiceContext;
import com.infonow.framework.service.support.ServiceException;
import com.infonow.service.configuration.ConfigurationException;
import com.infonow.service.configuration.impl.AbstractBaseServiceWithConfiguration;
import com.infonow.service.recovery.TranslationOrphan;
import com.modeln.cdm.integration.aws.AmazonSNSClientImpl;
import com.modeln.cdm.service.translation.configuration.TranslationConfguration;

import software.amazon.awssdk.utils.StringUtils;

@ServiceDefinition(name = "translationService", abstraction = com.modeln.cdm.service.translation.TranslationService.class, implementation = com.modeln.cdm.service.translation.TranslationServiceImpl.class)
public class TranslationServiceImpl extends AbstractBaseServiceWithConfiguration implements TranslationService
{
    private InowProfileRelationService inowProfileRelationService;

    private RecoveryQueries recoveryQueries;

    private AmazonSNSClientImpl awsSnsClient;

    private String awsSnsTopic;

    private InowProfileDao inowProfileDao;

    private static String UNWANTED_CHARS;

    private static String LATIN_CHARS;

    private static Pattern UNWANTED_PATTERN;

    private static Pattern LATIN_PATTERN;

    private TranslationConfguration translationConfig;

    Matcher matcher;

    public TranslationServiceImpl()
    {
        if (UNWANTED_CHARS == null)
        {
            UNWANTED_CHARS = "[\\u0020-\\u002F\\u003A-\\u0040\\u005B-\\u0060\\u007B-\\u007E\\u0030-\\u0039\\u00A0-\\u00BF\\u2013-\\u204A]";
        }

        if (LATIN_CHARS == null)
        {
            LATIN_CHARS = "[\\u0041-\\u005A\\u0061-\\u007A\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u00FF\\uFF00-\\uFF60]+";
        }

        if (UNWANTED_PATTERN == null)
        {
            UNWANTED_PATTERN = Pattern.compile(UNWANTED_CHARS);
        }

        if (LATIN_PATTERN == null)
        {
            LATIN_PATTERN = Pattern.compile(LATIN_CHARS);
        }
    }

    public RecoveryQueries getRecoveryQueries()
    {
        if (recoveryQueries == null)
        {
            throw new IllegalStateException("The attribute _recoveryQueries can not be null");
        }
        return recoveryQueries;
    }

    public void setRecoveryQueries(RecoveryQueries recoveryQueries)
    {
        this.recoveryQueries = recoveryQueries;
    }

    @SuppressWarnings("unchecked")
    @Override
    public ServiceResponse execute(ServiceContext serviceContext, Object userObject, Object userData)
            throws ServiceException
    {
        if (!(userData instanceof List<?>))
        {
            throw new IllegalArgumentException("Argument userData must be a List but was " + userData == null ? null
                    : userData.getClass().getName());
        }
        List<TranslationOrphan> ids = ((List<TranslationOrphan>) userData);
        List<JSONObject> addrs = new ArrayList<JSONObject>();
        translationConfig = getTranslationConfig();

        List<Long> inProgressIds = new ArrayList<Long>();
        for (int i = 0; i < ids.size(); i++)
        {
            Long sid = ids.get(i).getSid();
            InowProfileImpl inowProfile = (InowProfileImpl) getInowProfileDao().getBySid(ids.get(i).getInowProfileSid());

            boolean entityNeedsTranslation = isNonLatin(inowProfile.getName());

            String address = inowProfile.getStreet1() + inowProfile.getStreet2() + inowProfile.getCity()
                    + inowProfile.getStateProvince() + inowProfile.getPostalCode();
            boolean addressNeedsTranslation = isNonLatin(address);

            JSONObject addr = createTranslationInput(inowProfile, entityNeedsTranslation, addressNeedsTranslation);
            if (addr != null)
            {
                addrs.add(addr);
                inProgressIds.add(sid);
            }
        }
        if(inProgressIds.size() > 0)
        {
            for(int j = 0; j<inProgressIds.size();j++){
                ids.remove(inProgressIds.get(j));
            }
        }
        if (!addrs.isEmpty())
        {
            publish(addrs);
            changeStatus(inProgressIds, TranslationOrphanStatusEnum.IN_PROGRESS.name());
        }

        return new ServiceResponse("Success", ServiceResponse.SUCCESS);
    }

    public void changeStatus(List<Long> inProgressIds, String status) {
        getRecoveryQueries().updateStatusTranslationOrphans(status, inProgressIds);
    }

    protected JSONObject createTranslationInput(InowProfileImpl inowProfile, boolean entityNeedsTranslation,
            boolean addressNeedsTranslation) throws ServiceException
    {
        JSONObject addr = null;

        if ((entityNeedsTranslation || addressNeedsTranslation)
                && isTranslationNeededForCountry(inowProfile.getCountryObject().getTwoCharCode()) && !isInowRelationAlreadyExists(inowProfile))
        {
            addr = new JSONObject();
            try
            {
                addr.put("id", inowProfile.getIdentifier());

                JSONObject addrDetails = new JSONObject();

                if (entityNeedsTranslation)
                {
                    if (!StringUtils.isEmpty(inowProfile.getName()))
                    {
                        addrDetails.put("name", inowProfile.getName());
                    }
                }
                if (addressNeedsTranslation)
                {
                    if (!StringUtils.isEmpty(inowProfile.getStreet1()))
                    {
                        addrDetails.put("street1", inowProfile.getStreet1());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getStreet2()))
                    {
                        addrDetails.put("street2", inowProfile.getStreet2());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getCity()))
                    {
                        addrDetails.put("city", inowProfile.getCity());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getStateProvince()))
                    {
                        addrDetails.put("stateprovince", inowProfile.getStateProvince());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getPostalCode()))
                    {
                        addrDetails.put("postalcode", inowProfile.getPostalCode());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getCountry()))
                    {
                        addrDetails.put("country", inowProfile.getCountryObject().getTwoCharCode());
                    }
                }

                addr.put("query", addrDetails);
                addr.put("target", "EN");
            }
            catch (JSONException e)
            {
                throw new ServiceException("Exception while converting InowProfile to JSONObject" + e);
            }
        }

        return addr;

    }

    public void publish(List<JSONObject> addrs) throws ServiceException
    {
        try
        {
            getAwsSnsClient().publishSingleMessages(getAwsSnsTopic(), addrs);
        }
        catch (Exception e)
        {
            throw new ServiceException("Exception while publishing message to Amazon SNS", e);
        }
    }

    private boolean isTranslationNeededForCountry(String country)
    {
        return translationConfig.getCountries().contains(country);
    }

    private boolean isNonLatin(String addressComponent)
    {
        addressComponent = addressComponent.replace("null", "");

        matcher = UNWANTED_PATTERN.matcher(addressComponent);
        String fulladdress = matcher.replaceAll("");

        if (StringUtils.isEmpty(fulladdress))
        {
            return false;
        }

        String latinAddress = "";

        matcher = LATIN_PATTERN.matcher(fulladdress);
        while (matcher.find())
        {
            latinAddress += matcher.group();
        }

        float matchPercent = (latinAddress.length() * 100 / fulladdress.length());

        return matchPercent < translationConfig.getThreshold();
    }

    private boolean isInowRelationAlreadyExists(InowProfileImpl inowProfile)
    {
        return getInowProfileRelationService().isRelationExists(inowProfile.getSid());
    }

    private TranslationConfguration getTranslationConfig()
    {
        ServiceContext configContext = new ServiceContext(null, null);

        try
        {
            return (TranslationConfguration) getRequiredConfiguration(configContext);
        }
        catch (ConfigurationException e)
        {
            throw new IllegalStateException(e);
        }
    }

    public AmazonSNSClientImpl getAwsSnsClient()
    {
        return awsSnsClient;
    }

    public void setAwsSnsClient(AmazonSNSClientImpl awsSnsClient)
    {
        this.awsSnsClient = awsSnsClient;
    }

    public String getAwsSnsTopic()
    {
        return awsSnsTopic;
    }

    public void setAwsSnsTopic(String awsSnsTopic)
    {
        this.awsSnsTopic = awsSnsTopic;
    }

    public InowProfileDao getInowProfileDao()
    {
        return inowProfileDao;
    }

    public void setInowProfileDao(InowProfileDao inowProfileDao)
    {
        this.inowProfileDao = inowProfileDao;
    }

    public InowProfileRelationService getInowProfileRelationService()
    {
        return inowProfileRelationService;
    }

    public void setInowProfileRelationService(InowProfileRelationService inowProfileRelationService)
    {
        this.inowProfileRelationService = inowProfileRelationService;
    }

}
SELECT 
    CASE 
        WHEN column1 IS NULL OR column1 = 0 THEN column2
        ELSE column1
    END AS result_column
FROM example_table;


SELECT 
    COALESCE(NULLIF(column1, 0), column2) AS result_column
FROM example_table;

AND CAST(CC.EDITDATE AS DATE) > CAST(DATEADD(DAY, -1, GETDATE()) AS DATE)
--AND CONVERT(DATE,(CC.EDITDATE)) = CONVERT(DATE, DATEADD(DAY, -30 , GETDATE()))
WITH RECURSIVE date_range AS (
    SELECT date'2019-01-03' dateval, 0 mes  /*start date, use cast(? as date) if you need a parameter */
    FROM rdb$database
    UNION ALL 
    SELECT DATEADD(MONTH,1,dateval), mes + 1
    FROM date_range
    WHERE mes < 12 /* end date */
)
SELECT *
FROM date_range;
SHOW search_path;
SET search_path TO Schema1, public;
INSERT INTO `logs` (`id`, `Log`, `Info`, `Ip`, `Date`, `sn`) VALUES (NULL, '<a href=\"?limit=1000&player=Sam_Mason\"><strong>Sam_Mason</strong></a> Получил Анти-Варн', '<code><strong>I:</strong></code> <code>200,000,000</code> / <code>500,000,000</code> / <code>10000</code>', '<td><div class=\"table-ip\"><strong><code>I:</code></strong><span class=\"badge badge-secondary\">127.0.1.1</span><span class=\"badge badge-primary\">127.0.0.1</span></div><div class=\"table-ip\"><strong><code>II:</code></strong> <span class=\"badge badge-secondary\">127.0.0.1</span><span class=\"badge badge-primary\">127.0.0.1</span></div></td>', '2024-06-28 05:00:00', '1')
The Alphacodez Cryptocurrency Exchange Script is a robust solution for launching secure and efficient trading platforms, featuring user management, a high-performance trading engine, and wallet integration. It offers advanced security measures and customization options. Ongoing support and maintenance ensure smooth operation.
SELECT uid, pid, ExtractValue(pi_flexform,'//field[@index="settings.flexform.sender.subject"]/value') as psubject,  ExtractValue(pi_flexform,'//field[@index="settings.flexform.main.form"]/value') as pform FRoM tt_content WHERE CType LIKE "%powermail%" OR list_type LIKE "%powermail%" 
EXEC sp_spaceused N'[dbo].[MY_TABLE]'
package com.modeln.cdm.service.translation;

public enum TranslationOrphanStatusEnum
{
    PENDING,
    IN_PROGRESS;
}
/**
 * RecoveryQueriesImpl.java $Revision$
 *
 * Copyright (c) 2005-2008 InfoNow Corporation. All rights reserved.
 * INFONOW PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.infonow.service.recovery;

import com.infonow.crux.DataFile;
import com.infonow.crux.composite.CompositeAccountClassificationOrphan;
import com.infonow.crux.dao.jdbc.mapper.GenericMapper;
import com.infonow.crux.dao.jdbc.mapper.GenericRowMapper;
import com.infonow.crux.dataFile.DataFileOrphanId;
import com.infonow.crux.learning.LearnedName;
import com.infonow.crux.learning.LearnedName.ApprovalStatus;
import com.infonow.crux.learning.LearnedNameOrphan;
import com.infonow.crux.schedule.submissionSchedule.SubmissionScheduleOrphan;
import com.infonow.crux.sqlGeneration.SqlGenerationUtils;
import com.infonow.crux.util.CollectionUtils;
import com.infonow.crux.util.Constants.DataAddressTypeEnum;
import com.infonow.framework.logging.Logger;
import com.infonow.framework.util.spring.JdbcTemplateProxy;
import com.infonow.framework.util.spring.NamedParameterJdbcTemplateProxy;
import com.infonow.service.submissionPeriodUpdate.SubmissionPeriodUpdateOrphanId;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import org.apache.commons.lang.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;


/**
 * Query implementations for the recovery service.
 * 
 * @author Chris Lambrecht
 * 
 * @version $Revision$
 */
@SuppressWarnings("rawtypes")
public class RecoveryQueriesImpl implements RecoveryQueries
{
    private static final Logger LOG = Logger.getLogger(RecoveryQueriesImpl.class.getName());
    private static final String DELETE_RL_ORPHAN = "delete from rl_orphans where master_run_id = ?";
    private final RowMapper SID_MAPPER = new SidRowMapper();
    private final RowMapper LINE_ITEM_MAPPER = new LineItemRowMapper();
    private final RowMapper VALIDATION_ITEM_MAPPER = new ValidationRowMapper();
    private final RowMapper SERIAL_NUMBER_MAPPER = new SerialNumberRowMapper();
    private final RowMapper TUPPLE_MAPPER = new TupleMapper();
    private final RowMapper SLIA_MAPPER = new SliaProfileMapper();
    private final RowMapper INOW_PROFILE_HIERARCHY_MAPPER = new InowProfileHierarchyRowMapper();
    private final RowMapper HIERARCHY_PARENT_MAPPER = new HierarchyParentMapper();
    private final RowMapper ADDRESS_HISTORY_MAPPER = new AddressHistoryMapper();
    private final RowMapper INOW_PROFILE_MAPPER = new InowProfileRowMapper();
    private final RowMapper SALES_LINE_ITEM_NORMALIZATION_MAPPER = new SalesLineItemNormalizationRowMapper();
    private final RowMapper REPORTING_PARTNER_MAPPER = new ReportingPartnerRowMapper();
    private final RowMapper PROFILE_PARTNER_MAPPER = new ProfilePartnerRowMapper();
    private final RowMapper EXPORTER_ORPHAN_MAPPER = new ExporterOrphanRowMapper();
    private final RowMapper COMPOSITE_ACCOUNT_CLASS_ORPHAN_MAPPER = new CompositeAccountClassificationOrphanMapper();
    private final RowMapper SUBMISSION_SCHEDULE_ORPHAN_MAPPER = new SubmissionScheduleOrphanMapper();
    private final RowMapper LEARNED_NAME_ORPHAN_MAPPER = new LearnedNameOrphanMapper();
    private final RowMapper INOW_STANDARDIZATION_ORPHAN_MAPPER = new InowStandardizationOrphanMapper();
    private final RowMapper INOW_TRANSLATION_ORPHAN_MAPPER = new InowTranslationOrphanMapper();
    private final RowMapper SUBMISSION_SCHEDULE_UPDATE_ORPHAN_MAPPER = new SubmissionPeriodUpdateOrphanMapper();
    private final RowMapper INOW_RELATION_ORPHAN_MAPPER = new InowRelationOrphanMapper();
    private final RowMapper EXPORT_OUTPUT_ORPHAN_MAPPER = new ExportOutputOrphanMapper();
    private final RowMapper VALIDATION_TRACKING_ORPHAN_MAPPER = new ValidationTrackingRowMapper();
    private final RowMapper VALIDATION_DELETED_ORPHAN_MAPPER = new ValidationDeletedRowMapper();
    private JdbcTemplate template;
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    /**
     * Default Constructor
     */
    public RecoveryQueriesImpl()
    {
        super();
    }

    /**
     * Get the value of the template field.
     * 
     * @return Returns the value of template.
     */
    public JdbcTemplate getTemplate()
    {
        return this.template;
    }

    /**
     * Set the value of the template field.
     * 
     * @param template
     *            The template value to set.
     */
    public void setTemplate(JdbcTemplate template)
    {
        this.template = template;
    }

    /**
     * Get the value of the namedParameterJdbcTemplate field.
     * 
     * @return Returns the value of namedParameterJdbcTemplate.
     */
    public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate()
    {
        return this.namedParameterJdbcTemplate;
    }

    /**
     * Set the value of the namedParameterJdbcTemplate field.
     * 
     * @param namedParameterJdbcTemplate
     *            The namedParameterJdbcTemplate value to set.
     */
    public void setNamedParameterJdbcTemplate(NamedParameterJdbcTemplate namedParameterJdbcTemplate)
    {
        this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
    }

    /**
     * Inject a reference to the {@link DataSource} to use for all queries
     * 
     * @param dataSource
     *            - database connect source
     */
    public void setDataSource(DataSource dataSource)
    {
        template = new JdbcTemplateProxy(dataSource);
        namedParameterJdbcTemplate = new NamedParameterJdbcTemplateProxy(dataSource);
    }

    /**
     * Selects input file ids from the orphan table. Limit the number of rows returned to numIds and make sure that the
     * ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<Long> getParserOrphans(int numIds, int maxPerCustomer)
    {
        final String sql = "select data_file_sid from ( "
                + "select * from ( "
                + "select ifo.*, "
                + "       rank() over( partition by ifo.customer_sid order by sid ) rank_overall "
                + "from ( "
                + "select ifo.*, "
                + " rank() over( partition by ifo.customer_sid, ifo.type order by nvl(ifo.update_date,ifo.create_date) ) rank_of_type "
                + "from ( " + "select ifo.data_file_sid, " + "       if.customer_sid, " + "       ifo.sid, "
                + "       ifo.process_time, if.create_date, if.update_date, "
                + "       CASE WHEN (instr(upper(data_type),'CATALOG') > 0) THEN 'CATALOG' "
                + "            WHEN (instr(upper(data_type),'PROFILE') > 0) THEN 'PROFILE' "
                + "            ELSE data_type  " + "       END as type " + "from input_file_orphans ifo "
                + "join data_file if on ifo.data_file_sid = if.sid " + "where process_time < sysdate " + ") ifo "
                + ") ifo " + "where ifo.type not in ('CATALOG','PROFILE')  " + "      or ifo.rank_of_type = 1 " + ")  "
                + "where rank_overall <= ? " + "order by rank_overall " + ") where rownum < ? ";

        return getTemplate().query(sql, new Object[]
        { maxPerCustomer, numIds }, SID_MAPPER);
    }

    /**
     * Select sales product ids from the orphan table. Limit the number of rows returned to numIds and makes sure that
     * the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<LineItemOrphanId> getSalesProductOrphanIds(int numIds)
    {

        return getIds("sales_line_item_orphans", new String[]
        { "sid", "sales_line_item_sid", "match_type", "customer_sid" }, null, numIds, LINE_ITEM_MAPPER);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#getSalesLineItemSerialNumberOrphanIds(int,
     * com.infonow.service.recovery.SerialNumberOrphanId)
     */
    @SuppressWarnings("unchecked")
    public List<SerialNumberOrphanId> getSalesLineItemSerialNumberOrphanIds(int numIds)
    {
        return getIds("SLI_SERIAL_NUMBER_ORPHANS", new String[]
        { "sid", "sli_serial_number_sid", "match_type", "customer_sid" }, null, numIds, SERIAL_NUMBER_MAPPER);
    }

    /**
     * Select inv product ids from the orphan table. Limit the number of rows returned to numIds and makes sure that the
     * ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<LineItemOrphanId> getInvProductOrphanIds(int numIds)
    {
        return getIds("inv_line_item_orphans", new String[]
        { "sid", "inv_line_item_sid", "match_type", "customer_sid" }, null, numIds, LINE_ITEM_MAPPER);
    }

    /**
     * Select Profile Partner ids from the orphan table. Limit the number of rows returned to numIds and makes sure that
     * the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<ProfilePartnerOrphanId> getProfilePartnerOrphanIds(int numIds)
    {
        final String query = "SELECT" + 
                "    sid," + 
                "    profile_partner_sid," + 
                "    customer_sid," + 
                "    match_type," +
                "    retry_count" +
                " FROM" + 
                "    (" + 
                "        SELECT" + 
                "            *" + 
                "        FROM" + 
                "            (" + 
                "                SELECT" + 
                "                    sid," + 
                "                    profile_partner_sid," + 
                "                    customer_sid," + 
                "                    match_type," + 
                "                    retry_count," +
                "                    RANK() OVER(" + 
                "                        PARTITION BY customer_sid" + 
                "                        ORDER BY" + 
                "                            pp_create_date" +
                "                    ) rank" + 
                "                FROM" + 
                "                    (" + 
                "                        SELECT" + 
                "                            po.sid," + 
                "                            po.profile_partner_sid," + 
                "                            pp.customer_sid," + 
                "                            po.match_type," + 
                "                            po.retry_count," +
                "                            pp.create_date pp_create_date" +
                "                        FROM" + 
                "                            profile_orphans po" + 
                "                            JOIN profile_partner pp ON pp.sid = po.profile_partner_sid" + 
                "                        WHERE" + 
                "                            process_time < SYSDATE AND (pp.address_sid is null or pp.address_sid = 0)" + 
                "                            " + 
                "                        UNION " + 
                "                        " + 
                "                         SELECT" + 
                "                            po.sid," + 
                "                            po.profile_partner_sid," + 
                "                            pp.customer_sid," + 
                "                            po.match_type," +
                "                            po.retry_count," +
                "                            pp.create_date pp_create_date" +
                "                        FROM" + 
                "                            profile_orphans po" + 
                "                            JOIN profile_partner pp ON pp.sid = po.profile_partner_sid" + 
                "                            JOIN address_history ah ON ah.address_sid = pp.address_sid" + 
                "                        WHERE" + 
                "                            process_time < SYSDATE AND ah.stage = 'API_ENRICHED' " + 
                "                    )" + 
                "            )" + 
                "        ORDER BY" + 
                "            rank" + 
                "    )" + 
                "WHERE" + 
                "    ROWNUM < ?";

        return getTemplate().query(query, new Object[] { numIds }, PROFILE_PARTNER_MAPPER);
    }

    /**
     * Select Sales Record Address ids from the orphan table. Limit the number of rows returned to numIds and makes sure
     * that the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<SalesLineItemAddressOrphanId> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaProfileOrphanIds(int numIds)
    {
        final String query = "select * from (select * from (select sliapo.sid, "
                + "       sliapo.sales_line_item_sid, " + "       sliapo.address_Sid, " + "       sliapo.address_type, "
                + "       sliapo.match_type, sliapo.customer_sid, sli.BILL_TO_ADDR_EXTERNAL_ID"
                + "       ,sli.SOLD_TO_ADDR_EXTERNAL_ID, sli.SHIP_TO_ADDR_EXTERNAL_ID"
                + "       ,sli.SELL_FROM_ADDR_EXTERNAL_ID, sli.SHIP_FROM_ADDR_EXTERNAL_ID"
                + "       ,sli.SALES_IN_ADDR_EXTERNAL_ID, sli.PURCH_CUST_ADDR_EXTERNAL_ID"
                + "       ,sli.DER_END_CUST_ADDR_EXTERNAL_ID,sli.deleted "
                + "       ,case  when exists (select 1 from address_history ah where ah.address_sid = sliapo.address_sid and ah.stage = 'RAW')"
                + "               then 1 else 0 end raw_exists "
                + "       ,case  when exists (select 1 from address_history ah where ah.address_sid = sliapo.address_sid and ah.stage = 'REF_DATA_ENRICHED') "
                + "               then 1 else 0 end ref_data_exists,"
                + "       rank() over(partition by sliapo.customer_sid order by sliapo.sid) rank "
                + " from slia_profile_orphans sliapo join sales_line_item sli on sli.sid = sliapo.sales_line_item_sid and "
                + " sli.customer_sid = sliapo.customer_sid where process_time < sysdate"
                + " order by rank,address_sid) where (raw_exists = 0 or (raw_exists = 1 and ref_data_exists = 1))) where rownum < ? ";

        return getTemplate().query(query, new Object[] { numIds }, SLIA_MAPPER);

    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#getSliaCompositeAccountOrphanIds(int)
     */
    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaCompositeAccountOrphanIds(int numIds)
    {

        return getIds("slia_composite_acct_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "match_type", "customer_sid" }, null, numIds,
        TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<InowProfileHierarchyOrphanId> getInowProfileHierarchyOrphanIds(int numToRead)
    {

        final String query = "SELECT" + 
                "    * " + 
                " FROM" + 
                "    (" + 
                "        SELECT" + 
                "            *" + 
                "        FROM" + 
                "            (" + 
                "                SELECT" + 
                "                    ipho.sid," + 
                "                    ipho.inow_profile_sid," + 
                "                    ipho.customer_sid," + 
                "                    ipho.named_ip_hierarchy_sid," + 
                "                    ipho.reprocess_flag," + 
                "                    ipho.retry_count," + 
                "                    ipho.priority," + 
                "                    RANK() OVER(" + 
                "                        PARTITION BY ipho.customer_sid" + 
                "                        ORDER BY" + 
                "                            sid ASC" + 
                "                    ) rank," +
                "                    upper(csr.entity_name)" + 
                "                    || '-----'" + 
                "                    || csr.country orphan_key" + 
                "                FROM" + 
                "                    inow_profile_hierarchy_orphans   ipho" + 
                "                    LEFT JOIN csr_overlay_v                    csr ON csr.customer_sid = ipho.customer_sid" + 
                "                                              AND ipho.inow_profile_sid = csr.ip_sid" + 
                "                WHERE" + 
                "                    process_time < sysdate" + 
                "            )" + 
                "        ORDER BY" + 
                "            priority," + 
                "            rank" + 
                "    ) " + 
                " WHERE " + 
                "    ROWNUM < :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, INOW_PROFILE_HIERARCHY_MAPPER);

    }

    @SuppressWarnings("unchecked")
    public List<HierarchyParentOrphanId> getHierarchyParentOrphanIds(int numToRead)
    {

        final String query = "select * from ( " + "select * from ( " + "select hpo.sid, "
                + "       hpo.inow_profile_sid, " + "       hpo.customer_sid, "
                + "       hpo.named_ip_hierarchy_sid, " + "       hpo.reprocess_flag, " + "       hpo.retry_count, "
                + "       hpo.priority, "
                + "       rank() over( partition by hpo.customer_sid order by sid asc) rank "
                + "from hierarchy_parent_orphans hpo " + "where process_time < sysdate) "
                + "order by priority,rank " + ") " + "where rownum < :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, HIERARCHY_PARENT_MAPPER);

    }
    
    @SuppressWarnings("unchecked")
    public List<AddressHistoryOrphanId> getAddressHistoryOrphanIds(int numToRead, String tableName)
    {

        final String query = "select * from ( " + "select * from ( " + "select aho.sid, "
                + "       aho.address_history_sid, aho.retry_count "
                + "from " + tableName + " aho " + "where process_time < sysdate) "
                + "order by sid " + ") " + "where rownum <= :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, ADDRESS_HISTORY_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<SalesLineItemNormalizationOrphanId> getSalesLineItemNormalizationOrphanIds(int numToRead)
    {
        return getIds("sli_normalization_orphans", new String[]
        { "sid", "sales_line_item_sid", "customer_sid", "price_type, entity_sid" }, null, numToRead,
                SALES_LINE_ITEM_NORMALIZATION_MAPPER);
    }

    /**
     * Select Sales Record Address ids from the orphan table. Limit the number of rows returned to numIds and makes sure
     * that the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<SalesLineItemAddressOrphanId> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaReportingPartnerOrphanIds(int numIds)
    {
        return getIds("reporting_partner_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "null" }, null, numIds, TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationOrphanId> getSalesLineItemValidationOrphanIds(int numToRead)
    {

        final String query = "select * from ( " + "select sv.sid,sv.sales_line_item_sid,sv.match_type,sv.customer_sid, "
                + " sli.reporting_partner_sid,sli.data_file_sid,rank() over(partition by sv.customer_sid order by sv.create_date) rank_by_cust" 
                + " from sli_validation_orphans sv"
                + " join sales_line_item sli on sli.sid = sv.sales_line_item_sid "
                + " and sli.customer_sid = sli.customer_Sid where process_time < sysdate order by rank_by_cust,sv.create_date " 
                + ") where rownum <= :numToRead ";
        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, VALIDATION_ITEM_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaValidationOrphanIds(int numToRead)
    {
        return getIds("slia_validation_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "match_type", "customer_sid" }, null, numToRead,
        TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationTrackingOrphanId> getValidationTrackingOrphanIds(int numToRead)
    {
        return getIds("validation_tracking_orphans", new String[]
                        { "sid", "data_file_sid", "customer_sid",
                                "create_date", "update_date", "retry_count" }, null, numToRead,
                VALIDATION_TRACKING_ORPHAN_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationNotificationOrphanId> getValidationNotificationOrphanIds(int numToRead)
    {
        return getIds("validation_notification_orphans", new String[]
                        { "sid", "data_file_sid", "customer_sid",
                                "create_date", "update_date", "retry_count" }, null, numToRead,
                VALIDATION_DELETED_ORPHAN_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationOrphanId> getInventoryLineItemValidationOrphanIds(int numToRead)
    {
        final String query = "select * from ( " + "select sv.sid,sv.inv_line_item_sid,sv.match_type,sv.customer_sid, "
                + " ili.reporting_partner_sid,ili.data_file_sid,rank() over(partition by sv.customer_sid order by sv.create_date) rank_by_cust" 
                + " from ili_validation_orphans sv"
                + " join inv_line_item ili on ili.sid = sv.inv_line_item_sid "
                + " and ili.customer_sid = ili.customer_Sid where process_time < sysdate order by rank_by_cust,sv.create_date " 
                + ") where rownum <= :numToRead ";
        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, VALIDATION_ITEM_MAPPER);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#getReportingPartnerOrphanIds(int)
     */
    @SuppressWarnings("unchecked")
    @Override
    public List<ReportingPartnerOrphanId> getReportingPartnerOrphanIds(int numToRead)
    {
        return getIds("reporting_partner_inow_orphs", new String[]
        { "sid", "reporting_partner_sid" }, null, numToRead, REPORTING_PARTNER_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Long> getAddressStandardizationOrphans(int numToRead)
    {
        return getIds("addr_standardization_orphans", new String[]
        { "address_sid" }, null, numToRead, SID_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<InowStandardizationOrphanId> getInowProfileStandardizationOrphans(int numToRead)
    {
        return getIds("inow_profile_std_orphans", new String[]
        { "sid", "inow_profile_sid", "retry_count" }, null, numToRead, INOW_STANDARDIZATION_ORPHAN_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<InowProfileOrphanId> getInowProfileDedupOrphans(int numToRead)
    {
        return getIds("inow_profile_dedup_orphans", new String[]
        { "sid", "inow_profile_sid", "customer_sid" }, null, numToRead, INOW_PROFILE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Long> getProfilePartnerRematchOrphanIds(int numToRead)
    {
        return getIds("profile_rematch_orphans", new String[]
        { "profile_partner_sid" }, null, numToRead, SID_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<SalesLineItemAddressOrphanId> getSliaRematchOrphanIds(int numToRead)
    {
        return getIds("slia_profile_rematch_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "match_type" }, null, numToRead, TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<ExporterOrphanId> getExporterOrphanIds(int numToRead)
    {
        return getIds("exporter_orphans", new String[]
        { "sid", "customer_sid", "export_name" }, null, numToRead, EXPORTER_ORPHAN_MAPPER);
    }

    /**
     * Deletes the specified orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteDataFileId(Long id)
    {
        if (id == null)
        {
            return;
        }

        deleteId("input_file_orphans", "data_file_sid", id);
    }

    /**
     * Deletes the specified orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSalesProductId(LineItemOrphanId id)
    {
        deleteId("sales_line_item_orphans", id.getSid());
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#deleteSalesLineItemSerialNumberId(com.infonow.service.recovery.
     * SerialNumberOrphanId)
     */
    public void deleteSalesLineItemSerialNumberId(SerialNumberOrphanId id)
    {
        deleteId("sli_serial_number_orphans", id.getSid());
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteProfilePartnerId(ProfilePartnerOrphanId id)
    {
        deleteId("profile_orphans", "sid", id.getSid());
    }

    /**
     * Deletes the specified orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteInvProductId(LineItemOrphanId id)
    {
        deleteId("inv_line_item_orphans", id.getSid());
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSliaProfileId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_profile_orphans", id.getSid());
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#deleteSliaCompositeAccountId(com.infonow.service.recovery.
     * SalesLineItemAddressOrphanId)
     */
    public void deleteSliaCompositeAccountId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_composite_acct_orphans", id.getSid());
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSliaReportingPartnerId(SalesLineItemAddressOrphanId id)
    {
        deleteId("reporting_partner_orphans", id.getSid());
    }

    public void deleteSalesLineItemValidationId(List<ValidationOrphanId> ids)
    {
        String sql = new String("delete from sli_validation_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    public void deleteValidationTrackingIds(List<ValidationTrackingOrphanId> ids)
    {
        String sql = new String("delete from validation_tracking_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    public void deleteValidationNotificationIds(List<ValidationNotificationOrphanId> ids)
    {
        String sql = new String("delete from validation_notification_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    public void deleteSliaValidationId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_validation_orphans", id.getSid());
    }

    public void deleteInventoryLineItemValidationId(List<ValidationOrphanId> ids)
    {
        String sql = new String("delete from ili_validation_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteSalesLineItemOrphans(List<Long> salesLineItemSids)
    {
        String sql = new String("delete from sales_line_item_orphans where sales_line_item_sid in (:sids)");

        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", salesLineItemSids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteSalesLineItemOrphans(DataFile dataFile)
    {
        String sql = new String("delete from sales_line_item_orphans where sales_line_item_sid in "
                + " (select sid from sales_line_item where data_file_sid = ? and customer_sid = "
                + dataFile.getCustomer().getSid().toString() + ")");

        getTemplate().update(sql, new Object[]
        { dataFile.getSid() });
    }

    @Override
    public void deleteInvLineItemOrphans(List<Long> invLineItemSids)
    {
        String sql = new String("delete from inv_line_item_orphans where inv_line_item_sid in (:sids)");
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", invLineItemSids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void updateStatusTranslationOrphans(String status, List<Long> translationOrphanSids)//pass the array of sids, and pass status
    {
        String sql = new String("update translation_orphans set status = :status where sid in (:sids)");
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("sids", translationOrphanSids);
        paramMap.put("status", status);
        try
        {
            getNamedParameterJdbcTemplate().update(sql, paramMap);
        }
        catch(Exception e){
            System.out.println(e.getMessage());
        }
    }

    public void deleteRegexStandardizationOrphans(List<AddressHistoryOrphanId> orphanIds)
    {
        String sql = new String("delete from regex_std_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }
    
    @Override
    public void deleteRefDataStandardizationOrphans(List<AddressHistoryOrphanId> orphanIds)
    {
        String sql = new String("delete from ref_data_std_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }
    
    @Override
    public void deleteAddressHistoryOrphans(List<AddressHistoryOrphanId> orphanIds, String orphanTable)
    {
        String sql = new String("delete from " + orphanTable + " where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }
    
    @Override
    public void deleteAddressGradingOrphans(List<AddressHistoryOrphanId> orphanIds)
    {
        String sql = new String("delete from address_grading_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteTranslateOrphans(List<TranslationOrphan> orphanIds)
    {
        for (List<TranslationOrphan> orphans : CollectionUtils.partition(orphanIds, 1000))
        {
            String sql = new String(
                "delete from translation_orphans where sid in (:sids)");
            List<Long> sids = new ArrayList<>();
            orphans.forEach(ad -> sids.add(ad.getSid()));
            Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
            paramMap.put("sids", sids);

            getNamedParameterJdbcTemplate().update(sql, paramMap);
        }
    }

    @Override
    public void deleteExportOutputOrphans(List<ExportOutputOrphanId> orphanIds)
    {

        String sql = new String(
            "delete from export_output_orphans where sid in (:sids)");
        Map<String, Object> paramMap = new HashMap<String, Object>();
        List<Long> sids = orphanIds.stream().map(o -> o.getSid())
            .collect(Collectors.toList());
        List<List<Long>> sidsPartition = CollectionUtils.partition(sids, 1000);
        for (List<Long> batch : sidsPartition)
        {
            paramMap.put("sids", sids);

            getNamedParameterJdbcTemplate().update(sql, paramMap);
        }
    }

    @Override
    public void deleteSubmissionPeriodUpdateOrphans(
        SubmissionPeriodUpdateOrphanId orphanId)
    {

        String sql = new String(
            "delete from SP_UPDATE_ORPHANS where sid in (:sid)");
        Map<String, Long> paramMap = new HashMap<String, Long>();
        paramMap.put("sid", orphanId.getSid());

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteInowRelationOrphan(InowRelationOrphan orphanId)
    {
        String sql = new String("delete from relation_orphans where sid = :sid");
        Map<String, Long> paramMap = new HashMap<String, Long>();
        paramMap.put("sid", orphanId.getSid());

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteAddressQualityOrphans(List<AddressHistoryOrphanId> orphanIds)
    {
        String sql = new String("delete from address_quality_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteInvLineItemOrphans(DataFile dataFile)
    {
        String sql = new String("delete from inv_line_item_orphans where inv_line_item_sid in "
                + " (select sid from inv_line_item where data_file_sid = ? and customer_sid = "
                + dataFile.getCustomer().getSid().toString() + ")");

        getTemplate().update(sql, new Object[]
        { dataFile.getSid() });
    }

    @Override
    public List<DataFileOrphanId> getDataFileDeleteOrphans(int numToRead)
    {
        List<?> orphanIds = null;

        orphanIds = getIds("data_file_delete_orphans", new String[]
        { "sid", "data_file_sid", "customer_sid", "reprocess", "user_login", "retry_count", "preserve_date" }, null, null, numToRead, new GenericRowMapper()
        {
            @Override
            public Object mapRow(ResultSet rs, int arg1) throws SQLException
            {

                DataFileOrphanId dfoi = new DataFileOrphanId();
                dfoi.setDataFileSid(this.getLong(rs, "data_file_sid"));
                dfoi.setCustomerSid(this.getLong(rs, "customer_sid"));
                dfoi.setReprocess(this.getBooleanValue(rs, "reprocess"));
                dfoi.setUserLogin(this.getString(rs, "user_login"));
                dfoi.setSid(this.getLong(rs, "sid"));
                dfoi.setRetryCount(this.getLong(rs, "retry_count"));
                dfoi.setPreserveDate(this.getBooleanValue(rs, "preserve_date"));
                return dfoi;
            }

        });

        return (List<DataFileOrphanId>) orphanIds;
    }

    @SuppressWarnings("unchecked")
    public List<TranslationOrphan> getTranslationOrphans(int numToRead, String status)
    {

        String query = "select * from ( select ipro.sid, ipro.inow_profile_sid, ipro.retry_count, ipro.status, rownum cnt "
                + "from translation_orphans ipro where process_time < sysdate "
                + "order by sid ) result where rownum <= :numToRead ";

        Map<String, String> paramMap = new HashMap<String, String>();
        paramMap.put("numToRead", String.valueOf(numToRead));
        if(status!=null){
            query = query + "and result.status = :status";
            paramMap.put("status",status);
        }

        return getNamedParameterJdbcTemplate().query(query, paramMap, INOW_TRANSLATION_ORPHAN_MAPPER);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<SubmissionPeriodUpdateOrphanId> getSubmissionPeriodUpdateOrphans(int numToRead)
    {

        final String query =
            "select * from ( select spuo.sid, spuo.customer_sid, spuo.old_start_date, spuo.retry_count, rownum cnt "
                + "from sp_update_orphans spuo where process_time < sysdate "
                + "order by sid ) where rownum <= :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap,
            SUBMISSION_SCHEDULE_UPDATE_ORPHAN_MAPPER);
    }

    @Override
    public List<ExportOutputOrphanId> getExportOutputOrphans(int numToRead)
    {
        final String query = "WITH rowlimit AS ("
            + "SELECT * FROM ("
            + "SELECT EXPORT_STATUS_SID FROM export_output eo JOIN export_output_orphans eoo "
            + "ON eoo.EXPORT_OUTPUT_SID= eo.sid AND eo.customer_sid = eoo.customer_sid "
            + "WHERE eoo.process_time < sysdate + interval '10' second "
            + "GROUP BY EXPORT_STATUS_SID ) "
            + "WHERE rownum < :numToRead ) "
            + "SELECT eoo.SID,eoo.CUSTOMER_SID,eoo.SESSION_ID,eoo.EXPORT_OUTPUT_SID,"
            + "eo.EXPORT_STATUS_SID,eoo.retry_count,es.export_name, eoo.export_request_sid"
            + ",RANK() OVER (PARTITION BY eo.customer_sid,eo.EXPORT_STATUS_SID ORDER BY eoo.sid ) rankof "
            + "FROM EXPORT_OUTPUT eo JOIN EXPORT_OUTPUT_ORPHANS eoo ON eoo.EXPORT_OUTPUT_SID = eo.sid "
            + "JOIN rowlimit ON rowlimit.export_status_sid = eo.EXPORT_STATUS_SID "
            + "join export_status es on es.sid = eo.export_status_sid and es.customer_sid = eo.customer_sid "
            + "ORDER BY rankof";

        Map<String, Object> params = new HashMap<>();
        params.put("numToRead", numToRead);
        return getNamedParameterJdbcTemplate().query(query, params,
            EXPORT_OUTPUT_ORPHAN_MAPPER);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<InowRelationOrphan> getInowRelationOrphans(int numToRead)
    {

        final String query =
            "select * from ( select ro.sid, ro.inow_profile_sid, ro.related_inow_profile_sid, ro.retry_count, rownum cnt "
                + "from relation_orphans ro where process_time < sysdate "
                + "order by sid ) where rownum <= :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap,
            INOW_RELATION_ORPHAN_MAPPER);
    }

    @Override
    public void deleteDataFileDeleteOrphanId(DataFileOrphanId id)
    {

        String sql = new String("delete from data_file_delete_orphans where sid = ? "
                + " and customer_sid = " + id.getCustomerSid());

        getTemplate().update(sql, new Object[]
        { id.getSid() });

    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#deleteReportingPartnerId(com.infonow.service.recovery.
     * ReportingPartnerOrphanId)
     */
    @Override
    public void deleteReportingPartnerId(ReportingPartnerOrphanId id)
    {
        if (id == null || id.getSid() == null || id.getSid() <= 0)
        {
            return;
        }

        deleteId("reporting_partner_inow_orphs", id.getSid());
    }

    public void deleteInowProfileHierarchyId(InowProfileHierarchyOrphanId id)
    {
        deleteId("inow_profile_hierarchy_orphans", id.getSid());
    }

    public void deleteHierarchyParentId(HierarchyParentOrphanId id)
    {
        deleteId("hierarchy_parent_orphans", id.getSid());
    }

    public void deleteHierarchyParentIds(List<HierarchyParentOrphanId> ids)
    {
        List<Long> sids = new ArrayList<Long>();
        
        for (HierarchyParentOrphanId id : ids) 
        {
            sids.add(id.getSid());            
        }
        
        deleteIds("hierarchy_parent_orphans", sids);
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSalesLineItemNormalizationId(SalesLineItemNormalizationOrphanId id)
    {
        deleteId("sli_normalization_orphans", id.getSid());
    }

    @Override
    public void deleteAddressStandardizationOrphan(Long id)
    {
        deleteId("addr_standardization_orphans", "address_sid", id);
    }

    @Override
    public void deleteInowProfileStandardizationOrphan(InowStandardizationOrphanId id)
    {
        deleteId("inow_profile_std_orphans", "inow_profile_sid", id.getInowProfileSid());
    }

    @Override
    public void deleteInowProfileDedupOrphan(InowProfileOrphanId id)
    {
        deleteId("inow_profile_dedup_orphans", id.getSid());
    }

    @Override
    public void deleteProfilePartnerRematchId(Long id)
    {
        deleteId("profile_rematch_orphans", "profile_partner_sid", id);
    }

    @Override
    public void deleteSliaRematchId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_profile_rematch_orphans", id.getSid());
    }

    @Override
    public void deleteExporterOrphan(ExporterOrphanId id)
    {
        deleteId("exporter_orphans", id.getSid());
    }

    /**
     * Dynamically generates a recovery orphan query for select
     * This function will alternate customers round robin if no orderByClause is specified
     * and there is customer_sid in the columns
     * @param tableName
     * @param columns
     * @param orderByClause
     * @param numIds
     * @param rowMapper
     * @return list of rowMaper
     */
    @SuppressWarnings("rawtypes")
    private List getIds(String tableName, String[] columns, String orderByClause, Integer numIds, RowMapper rowMapper)
    {
        return getIds(tableName, columns, null, orderByClause, numIds, rowMapper);
    }


    /**
     * Dynamically generates a recovery orphan query for select
     * This function will alternate customers round robin if no orderByClause is specified
     * and there is customer_sid in the columns
     * @param tableName
     * @param columns
     * @param additionalWhereClause
     * @param orderByClause
     * @param numIds
     * @param rowMapper
     * @return list of rowMaper
     */
    @SuppressWarnings("rawtypes")
    private List getIds(String tableName, String[] columns, String additionalWhereClause, String orderByClause,
            Integer numIds, RowMapper rowMapper)
    {
        if (columns == null || columns.length <= 0)
        {
            return null;
        }

        StringBuilder sqlBuffer = new StringBuilder("select * from (select ");

        for (int i = 0; i < columns.length; ++i)
        {
            if (i > 0)
            {
                sqlBuffer.append(", ");
            }

            sqlBuffer.append(columns[i]);

            // automatically implement customer 100 functions for all queues.
            if (StringUtils.equalsIgnoreCase(columns[i], "customer_sid"))
            {
                if (i > 0)
                {
                    sqlBuffer.append(", ");
                }

                sqlBuffer.append("rank() over(partition by customer_sid order by sid) rank_by_cust ");

                if (StringUtils.isEmpty(orderByClause))
                {
                    orderByClause = "order by rank_by_cust";
                }

            }

        }

        sqlBuffer.append(" from ");
        sqlBuffer.append(tableName);

        // Construct the WHERE clause
        sqlBuffer.append(" where process_time < sysdate ");
        if (!StringUtils.isEmpty(additionalWhereClause))
        {
            sqlBuffer.append(" and ");
            sqlBuffer.append(additionalWhereClause);
        }

        if (StringUtils.isEmpty(orderByClause))
        {
            sqlBuffer.append(" order by process_time desc ");
        }
        else
        {
            sqlBuffer.append(orderByClause);
        }

        sqlBuffer.append(" ) where rownum <= ?");

        String query = sqlBuffer.toString();

        return getTemplate().query(query, new Object[]
        { numIds }, rowMapper);
    }

    private void deleteId(String tableName, Long sid)
    {
        deleteId(tableName, null, sid);
    }

    private void deleteId(String tableName, String columnName, Long sid)
    {
        StringBuffer sqlBuffer = new StringBuffer("delete from ");

        sqlBuffer.append(tableName);

        if (StringUtils.isEmpty(columnName))
        {
            sqlBuffer.append(" where sid = ?");
        }
        else
        {
            sqlBuffer.append(" where ");
            sqlBuffer.append(columnName);
            sqlBuffer.append(" = ?");
        }

        template.update(sqlBuffer.toString(), new Object[]
        { sid });
    }

    private void deleteIds(String tableName, List<Long> sids)
    {
        for (List<Long> inList : CollectionUtils.partition(sids, 1000))
        {
            deleteIds(tableName, null, inList);
        }
    }

    private void deleteIds(String tableName, String columnName, List<Long> sids)
    {
        StringBuffer sqlBuffer = new StringBuffer("delete from ");

        sqlBuffer.append(tableName);

        if (StringUtils.isEmpty(columnName))
        {
            sqlBuffer.append(" where sid in ");
        }
        else
        {
            sqlBuffer.append(" where ");
            sqlBuffer.append(columnName);
            sqlBuffer.append(" in ");
        }
        
        String deleteQuery = SqlGenerationUtils.buildInClauseVariables(sqlBuffer.toString(), sids.size());
        
        List<Object> params = new ArrayList<Object>();
        params.addAll(sids);

        template.update(deleteQuery, params.toArray());
    }

    /**
     * RowMapper instance used to extract the sid from a query
     */
    private class SidRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return (rs.getLong(1));
        }
    }

    /**
     * RowMapper instance used to extract the line item from a query
     */
    private class LineItemRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            if (rs.getMetaData().getColumnCount() == 2)
            {
                // SID and SLI_SID
                return new LineItemOrphanId(rs.getLong(1), rs.getLong(2), null);
            }
            else
            {
                // SID, SLI_SID and MATCH_TYPE
                return new LineItemOrphanId(rs.getLong(1), rs.getLong(2), rs.getString(3));
            }
        }
    }
    
    /**
     * RowMapper instance used to extract the line item from a query
     */
    private class ValidationRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            ValidationOrphanId orphan = new ValidationOrphanId(rs.getLong(1), rs.getLong(2), rs.getString(3));
            orphan.setCustomerSid(rs.getLong(4));
            orphan.setReportingPartnerSid(rs.getLong(5));
            orphan.setDataFileSid(rs.getLong(6));
            return orphan;
            
        }
    }

    private class ValidationTrackingRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new ValidationTrackingOrphanId(rs.getLong("sid"),
                    rs.getLong("data_file_sid"),
                    rs.getLong("customer_sid"),
                    rs.getDate("create_date"),
                    rs.getDate("update_date"),
                    rs.getLong("retry_count"));

        }
    }

    private class ValidationDeletedRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new ValidationNotificationOrphanId(rs.getLong("sid"),
                    rs.getLong("data_file_sid"),
                    rs.getLong("customer_sid"),
                    rs.getDate("create_date"),
                    rs.getDate("update_date"),
                    rs.getLong("retry_count"));

        }
    }

    private class ReportingPartnerRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new ReportingPartnerOrphanId(rs.getLong(1), rs.getLong(2));
        }
    }

    /**
     * RowMapper instance used to extract the serial number from a query
     */
    private class SerialNumberRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new SerialNumberOrphanId(rs.getLong(1), rs.getLong(2), rs.getString(3));
        }
    }

    private class SliaProfileMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            SalesLineItemAddressOrphanId orphan = new SalesLineItemAddressOrphanId(rs.getLong(1), rs.getLong(2),
                    rs.getLong(3), DataAddressTypeEnum.fromDbCode(rs.getString(4)), rs.getString(5), rs.getLong(6));

            /*
             * the natural key for slia_profile orphans is the address, and any external id for that line item if it is
             * deleted and the customer this is because external id's / deleted line items have different code paths for
             * short circuit matching
             */
            StringBuilder sb = new StringBuilder();
            sb.append(orphan.getAddressSid());
            sb.append(orphan.getCustomerSid());
            // we need this so that we don't cause a race condition on saving the ami data in the amidao
            sb.append(orphan.getSalesLineItemSid());
            switch (orphan.getAddressType())
            {
                case BILL_TO:
                    sb.append(rs.getString("BILL_TO_ADDR_EXTERNAL_ID"));
                    break;
                case SHIP_TO:
                    sb.append(rs.getString("SHIP_TO_ADDR_EXTERNAL_ID"));
                    break;
                case SOLD_TO:
                    sb.append(rs.getString("SOLD_TO_ADDR_EXTERNAL_ID"));
                    break;
                case SHIP_FROM:
                    sb.append(rs.getString("SHIP_FROM_ADDR_EXTERNAL_ID"));
                    break;
                case SELL_FROM:
                    sb.append(rs.getString("SELL_FROM_ADDR_EXTERNAL_ID"));
                    break;
                case SALES_IN:
                    sb.append(rs.getString("SALES_IN_ADDR_EXTERNAL_ID"));
                    break;
                case PURCHASING_CUSTOMER:
                    sb.append(rs.getString("PURCH_CUST_ADDR_EXTERNAL_ID"));
                    break;
                case DERIVED_END_CUSTOMER:
                    sb.append(rs.getString("DER_END_CUST_ADDR_EXTERNAL_ID"));
                    break;
            }

            sb.append(rs.getInt("deleted"));

            orphan.setGroupByValue(sb.toString());

            return orphan;
        }
    }

    private class TupleMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {

            return new SalesLineItemAddressOrphanId(rs.getLong(1), rs.getLong(2), rs.getLong(3),
                    DataAddressTypeEnum.fromDbCode(rs.getString(4)), rs.getString(5), 0l);
        }
    }

    private class InowProfileHierarchyRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            InowProfileHierarchyOrphanId row = new InowProfileHierarchyOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setCustomerSid(rs.getLong("customer_sid"));
            if (rs.wasNull())
            {
                row.setCustomerSid(null);
            }
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));

            row.setHierarchySid(rs.getLong("named_ip_hierarchy_sid"));
            if (rs.wasNull())
            {
                row.setHierarchySid(null);
            }

            Long reprocess = rs.getLong("reprocess_flag");
            if (!rs.wasNull())
            {
                row.setReprocess(reprocess == 0 ? Boolean.FALSE : Boolean.TRUE);
            }

            row.setRetryCount(rs.getLong("retry_count"));
            row.setOrphanKey(rs.getString("orphan_key"));

            return row;
        }
    }
    
    private class HierarchyParentMapper extends InowProfileHierarchyRowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            
            HierarchyParentOrphanId row = new HierarchyParentOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setCustomerSid(rs.getLong("customer_sid"));
            if (rs.wasNull())
            {
                row.setCustomerSid(null);
            }
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));

            row.setHierarchySid(rs.getLong("named_ip_hierarchy_sid"));
            if (rs.wasNull())
            {
                row.setHierarchySid(null);
            }
            
            Long reprocess = rs.getLong("reprocess_flag");
            if (!rs.wasNull())
            {
                row.setReprocess(reprocess == 0 ? Boolean.FALSE : Boolean.TRUE);
            }

            row.setRetryCount(rs.getLong("retry_count"));
            
            row.setGroupByValue(rs.getLong("named_ip_hierarchy_sid"));

            return row;
        }
        
    }
    
    private class AddressHistoryMapper extends GenericMapper implements RowMapper<AddressHistoryOrphanId>
    {
        public AddressHistoryOrphanId mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            this.columnNames = null;
            AddressHistoryOrphanId row = new AddressHistoryOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setAddressHistorySid(rs.getLong("address_history_sid"));
            row.setRetryCount(rs.getLong("retry_count"));
            return row;
        }
    }

    private class InowStandardizationOrphanMapper implements RowMapper<InowStandardizationOrphanId>
    {
        public InowStandardizationOrphanId mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            InowStandardizationOrphanId row = new InowStandardizationOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));
            row.setRetryCount(rs.getLong("retry_count"));
            return row;
        }
    }

    private class InowProfileRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            InowProfileOrphanId row = new InowProfileOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setCustomerSid(rs.getLong("customer_sid"));
            if (rs.wasNull())
            {
                row.setCustomerSid(null);
            }
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));

            return row;
        }
    }

    private class SalesLineItemNormalizationRowMapper extends GenericRowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            Long sid = this.getLong(rs, "sid");
            Long salesLineItemSid = this.getLong(rs, "sales_line_item_sid");
            Long entitySid = this.getLong(rs, "entity_sid");
            Long customerSid = this.getLong(rs, "customer_sid");
            String priceType = this.getString(rs, "price_type");
            SalesLineItemNormalizationOrphanId sliNormOrph = new SalesLineItemNormalizationOrphanId(sid,
                    salesLineItemSid, customerSid, priceType, entitySid);
            return sliNormOrph;
        }
    }

    private class ProfilePartnerRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long customerSid = rs.getLong("customer_sid");
            Long profilePartnerSid = rs.getLong("profile_partner_sid");
            String matchType = rs.getString("match_type");
            ProfilePartnerOrphanId profileSummaryOrphan = new ProfilePartnerOrphanId(sid, customerSid,
                    profilePartnerSid, matchType);
            profileSummaryOrphan.setRetryCount(rs.getLong("retry_count"));
            return profileSummaryOrphan;
        }
    }

    private final class CompositeAccountClassificationOrphanMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int row) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long inowProfileSid = rs.getLong("inow_profile_sid");
            String classificationCode = rs.getString("classification_code");
            return new CompositeAccountClassificationOrphan(sid, inowProfileSid, classificationCode);
        }
    }

    private class ExporterOrphanRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long customerSid = rs.getLong("customer_sid");
            String exportName = rs.getString("export_name");
            ExporterOrphanId id = new ExporterOrphanId();
            id.setSid(sid);
            id.setCustomerSid(customerSid);
            id.setExportName(exportName);
            return id;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<CompositeAccountClassificationOrphan> getCompositeAccountClassificationOrphans(int numToRead)
    {
        return getIds("comp_acct_class_orphan", new String[]
        { "sid", "inow_profile_sid", "classification_code" }, null, numToRead, COMPOSITE_ACCOUNT_CLASS_ORPHAN_MAPPER);
    }

    @Override
    public void deleteCompositeAccountClassificationOrphan(CompositeAccountClassificationOrphan id)
    {
        deleteId("comp_acct_class_orphan", id.getSid());
    }

    private final class SubmissionScheduleOrphanMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int row) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long customerSid = rs.getLong("customer_sid");

            Long submissionPeriodSid = rs.getLong("submission_period_sid");
            if (rs.wasNull())
            {
                submissionPeriodSid = null;
            }

            Long dataFileSid = rs.getLong("data_file_sid");
            if (rs.wasNull())
            {
                dataFileSid = null;
            }

            SubmissionScheduleOrphan.Action action = SubmissionScheduleOrphan.Action.valueOf(rs.getString("action"));
            SubmissionScheduleOrphan orphan = new SubmissionScheduleOrphan(sid, customerSid, submissionPeriodSid,
                    dataFileSid, action);
            orphan.setProcessTime(rs.getTimestamp("process_time"));
            orphan.setCreateDate(rs.getTimestamp("create_date"));
            orphan.setRetryCount(rs.getLong("retry_count"));
            return orphan;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<SubmissionScheduleOrphan> getSubmissionScheduleOrphans(int numToRead)
    {
        // We only want to return one orphan per customer to avoid multi-threaded
        // contention issues with the database. For instance, 2 threads may need to
        // rebuild the same set of data_file_summary_info records for a given data_file.
        // We also want to give the CLEANUP action orphans a lower priority than
        // the normal orphans which is why a decode of the action shows up in the order by
        // within the analytical function row_number.
        String sql = "SELECT * FROM " + "( " + "    SELECT o.sid, " + "           o.customer_sid, "
                + "           o.submission_period_sid, " + "           o.data_file_sid, " + "           o.action, "
                + "           o.process_time, " + "           o.create_date, " + "           o.retry_count, "
                + "           row_number() over (partition by o.customer_sid " + "   order by "
                + "     decode(o.action,'CLEANUP',100,10)," + "     o.process_time) rn "
                + "      FROM submission_schedule_orphan o " + "     WHERE o.process_time < sysdate " + ") "
                + "WHERE rownum < ? " + "  AND rn = 1";

        List<SubmissionScheduleOrphan> orphans = getTemplate().query(sql, new Object[]
        { numToRead }, SUBMISSION_SCHEDULE_ORPHAN_MAPPER);

        return orphans;
    }

    @Override
    public void deleteSubmissionScheduleOrphan(SubmissionScheduleOrphan id)
    {
        deleteId("submission_schedule_orphan", id.getSid());
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<LearnedNameOrphan> getLearnedNameOrphans(int numToRead)
    {
        final String sql = "SELECT * FROM (SELECT o.sid as orphan_sid, l.sid as name_learning_sid, l.from_name, l.to_name, l.approval_status"
                + " FROM name_learning l"
                + " JOIN name_learning_orphan o ON o.name_learning_sid = l.sid"
                + " WHERE o.process_time < sysdate" + " ) WHERE rownum < ?";

        return getTemplate().query(sql, new Object[]
        { numToRead }, LEARNED_NAME_ORPHAN_MAPPER);
    }

    @Override
    public void deleteLearnedNameOrphan(LearnedNameOrphan orphan)
    {
        deleteId("name_learning_orphan", orphan.getSid());
    }

    private static class LearnedNameOrphanMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            LearnedName learnedName = new LearnedName(rs.getString("from_name"), rs.getString("to_name"));
            learnedName.setApprovalStatus(ApprovalStatus.fromString(rs.getString("approval_status")));
            learnedName.setSid(rs.getLong("name_learning_sid"));
            LearnedNameOrphan learnedNameOrphan = new LearnedNameOrphan(learnedName);
            learnedNameOrphan.setSid(rs.getLong("orphan_sid"));
            return learnedNameOrphan;
        }
    }
    
    private static class InowTranslationOrphanMapper implements RowMapper<TranslationOrphan>
    {
        public TranslationOrphan mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            TranslationOrphan translationOrphan = new TranslationOrphan();
            translationOrphan.setSid(rs.getLong("sid"));
            translationOrphan.setInowProfileSid(rs.getLong("inow_profile_sid"));
            translationOrphan.setRetryCount(rs.getLong("retry_count"));
            translationOrphan.setGroupByValue(Math.round(rs.getLong("cnt") / 10));
            return translationOrphan;
        }
    }
    
    private static class SubmissionPeriodUpdateOrphanMapper implements RowMapper<SubmissionPeriodUpdateOrphanId>
    {
        public SubmissionPeriodUpdateOrphanId mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            SubmissionPeriodUpdateOrphanId submissionPeriodUpdateOrphanId = new SubmissionPeriodUpdateOrphanId();
            submissionPeriodUpdateOrphanId.setSid(rs.getLong("sid"));
            submissionPeriodUpdateOrphanId.setCustomerSid(rs.getLong("customer_sid"));
            submissionPeriodUpdateOrphanId.setOldStartDate(rs.getDate("old_start_date"));
            submissionPeriodUpdateOrphanId.setRetryCount(rs.getLong("retry_count"));
            return submissionPeriodUpdateOrphanId;
        }
    }

    private static class InowRelationOrphanMapper
        implements RowMapper<InowRelationOrphan>
    {
        public InowRelationOrphan mapRow(ResultSet rs, int rowNum)
            throws SQLException
        {
            InowRelationOrphan inowRelationOrphan = new InowRelationOrphan();
            inowRelationOrphan.setSid(rs.getLong("sid"));
            inowRelationOrphan.setInowProfileSid(
                rs.getLong("inow_profile_sid"));
            inowRelationOrphan.setRelatedInowProfileSid(
                rs.getLong("related_inow_profile_sid"));
            inowRelationOrphan.setRetryCount(rs.getLong("retry_count"));
            return inowRelationOrphan;
        }
    }

    private static class ExportOutputOrphanMapper
        implements RowMapper<ExportOutputOrphanId>
    {
        public ExportOutputOrphanId mapRow(ResultSet rs, int rowNum)
            throws SQLException
        {
            ExportOutputOrphanId exportOutputOrphan = new ExportOutputOrphanId();
            exportOutputOrphan.setCustomerSid(rs.getLong("CUSTOMER_SID"));
            exportOutputOrphan.setExportOutputSid(
                rs.getLong("EXPORT_OUTPUT_SID"));
            exportOutputOrphan.setSid(rs.getLong("SID"));
            exportOutputOrphan.setRetryCount(rs.getLong("RETRY_COUNT"));
            exportOutputOrphan.setExportRequestSid(
                rs.getLong("EXPORT_REQUEST_SID"));
            exportOutputOrphan.setGroupByValue(rs.getLong("EXPORT_STATUS_SID"));
            exportOutputOrphan.setSessionId(rs.getString("SESSION_ID"));
            return exportOutputOrphan;
        }
    }
}
package com.modeln.cdm.service.translation;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.infonow.service.recovery.RecoveryQueries;
import org.json.JSONException;
import org.json.JSONObject;

import com.infonow.crux.dao.InowProfileDao;
import com.infonow.crux.impl.InowProfileImpl;
import com.infonow.framework.service.ServiceResponse;
import com.infonow.framework.service.configuration.support.ServiceDefinition;
import com.infonow.framework.service.context.ServiceContext;
import com.infonow.framework.service.support.ServiceException;
import com.infonow.service.configuration.ConfigurationException;
import com.infonow.service.configuration.impl.AbstractBaseServiceWithConfiguration;
import com.infonow.service.recovery.TranslationOrphan;
import com.modeln.cdm.integration.aws.AmazonSNSClientImpl;
import com.modeln.cdm.service.translation.configuration.TranslationConfguration;

import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import software.amazon.awssdk.utils.StringUtils;

@ServiceDefinition(name = "translationService", abstraction = com.modeln.cdm.service.translation.TranslationService.class, implementation = com.modeln.cdm.service.translation.TranslationServiceImpl.class)
public class TranslationServiceImpl extends AbstractBaseServiceWithConfiguration implements TranslationService
{
    private InowProfileRelationService inowProfileRelationService;

    private RecoveryQueries _recoveryQueries;

    private AmazonSNSClientImpl awsSnsClient;

    private String awsSnsTopic;

    private InowProfileDao inowProfileDao;

    private static String UNWANTED_CHARS;

    private static String LATIN_CHARS;

    private static Pattern UNWANTED_PATTERN;

    private static Pattern LATIN_PATTERN;

    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;


    private TranslationConfguration translationConfig;

    Matcher matcher;

    public TranslationServiceImpl()
    {
        if (UNWANTED_CHARS == null)
        {
            UNWANTED_CHARS = "[\\u0020-\\u002F\\u003A-\\u0040\\u005B-\\u0060\\u007B-\\u007E\\u0030-\\u0039\\u00A0-\\u00BF\\u2013-\\u204A]";
        }

        if (LATIN_CHARS == null)
        {
            LATIN_CHARS = "[\\u0041-\\u005A\\u0061-\\u007A\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u00FF\\uFF00-\\uFF60]+";
        }

        if (UNWANTED_PATTERN == null)
        {
            UNWANTED_PATTERN = Pattern.compile(UNWANTED_CHARS);
        }

        if (LATIN_PATTERN == null)
        {
            LATIN_PATTERN = Pattern.compile(LATIN_CHARS);
        }
    }

    public RecoveryQueries getRecoveryQueries()
    {
        if (_recoveryQueries == null)
        {
            throw new IllegalStateException("The attribute _recoveryQueries can not be null");
        }
        return _recoveryQueries;
    }

    public void setRecoveryQueries(RecoveryQueries recoveryQueries)
    {
        _recoveryQueries = recoveryQueries;
    }
    public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate()
    {
        return this.namedParameterJdbcTemplate;
    }

    @SuppressWarnings("unchecked")
    @Override
    public ServiceResponse execute(ServiceContext serviceContext, Object userObject, Object userData)
            throws ServiceException
    {
        if (!(userData instanceof List<?>))
        {
            throw new IllegalArgumentException("Argument userData must be a List but was " + userData == null ? null
                    : userData.getClass().getName());
        }
        List<TranslationOrphan> ids = ((List<TranslationOrphan>) userData);
        List<JSONObject> addrs = new ArrayList<JSONObject>();
        translationConfig = getTranslationConfig();

        List<Long> inProgressIds = new ArrayList<Long>();
        for (TranslationOrphan id : ids)
        {
            Long sid = id.getSid();
            InowProfileImpl inowProfile = (InowProfileImpl) getInowProfileDao().getBySid(id.getInowProfileSid());

            boolean entityNeedsTranslation = isNonLatin(inowProfile.getName());

            String address = inowProfile.getStreet1() + inowProfile.getStreet2() + inowProfile.getCity()
                    + inowProfile.getStateProvince() + inowProfile.getPostalCode();
            boolean addressNeedsTranslation = isNonLatin(address);

            JSONObject addr = createTranslationInput(inowProfile, entityNeedsTranslation, addressNeedsTranslation);
            inProgressIds.add(sid);
            if (addr != null)
            {
                addrs.add(addr);
                inProgressIds.add(sid);
                ids.remove(id);//since the ids which are in pending state must not be deleted
            }
        }
        changeStatus(TranslationOrphanStatusEnum.IN_PROGRESS.toString(), inProgressIds);
        if (!addrs.isEmpty())
        {
            publish(addrs);
            changeStatus(TranslationOrphanStatusEnum.IN_PROGRESS.toString(), inProgressIds);
        }

        return new ServiceResponse("Success", ServiceResponse.SUCCESS);
    }

    public void changeStatus(String status,List<Long> inProgressIds){
        getRecoveryQueries().updateStatusTranslationOrphans(status, inProgressIds);
    }

    protected JSONObject createTranslationInput(InowProfileImpl inowProfile, boolean entityNeedsTranslation,
            boolean addressNeedsTranslation) throws ServiceException
    {
        JSONObject addr = null;

        if ((entityNeedsTranslation || addressNeedsTranslation)
                && isTranslationNeededForCountry(inowProfile.getCountryObject().getTwoCharCode()) && !isInowRelationAlreadyExists(inowProfile))
        {
            addr = new JSONObject();
            try
            {
                addr.put("id", inowProfile.getIdentifier());

                JSONObject addrDetails = new JSONObject();

                if (entityNeedsTranslation)
                {
                    if (!StringUtils.isEmpty(inowProfile.getName()))
                    {
                        addrDetails.put("name", inowProfile.getName());
                    }
                }
                if (addressNeedsTranslation)
                {
                    if (!StringUtils.isEmpty(inowProfile.getStreet1()))
                    {
                        addrDetails.put("street1", inowProfile.getStreet1());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getStreet2()))
                    {
                        addrDetails.put("street2", inowProfile.getStreet2());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getCity()))
                    {
                        addrDetails.put("city", inowProfile.getCity());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getStateProvince()))
                    {
                        addrDetails.put("stateprovince", inowProfile.getStateProvince());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getPostalCode()))
                    {
                        addrDetails.put("postalcode", inowProfile.getPostalCode());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getCountry()))
                    {
                        addrDetails.put("country", inowProfile.getCountryObject().getTwoCharCode());
                    }
                }

                addr.put("query", addrDetails);
                addr.put("target", "EN");
            }
            catch (JSONException e)
            {
                throw new ServiceException("Exception while converting InowProfile to JSONObject" + e);
            }
        }

        return addr;

    }

    public void publish(List<JSONObject> addrs) throws ServiceException
    {
        try
        {
            getAwsSnsClient().publishSingleMessages(getAwsSnsTopic(), addrs);
        }
        catch (Exception e)
        {
            throw new ServiceException("Exception while publishing message to Amazon SNS", e);
        }
    }

    private boolean isTranslationNeededForCountry(String country)
    {
        return translationConfig.getCountries().contains(country);
    }

    private boolean isNonLatin(String addressComponent)
    {
        addressComponent = addressComponent.replace("null", "");

        matcher = UNWANTED_PATTERN.matcher(addressComponent);
        String fulladdress = matcher.replaceAll("");

        if (StringUtils.isEmpty(fulladdress))
        {
            return false;
        }

        String latinAddress = "";

        matcher = LATIN_PATTERN.matcher(fulladdress);
        while (matcher.find())
        {
            latinAddress += matcher.group();
        }

        float matchPercent = (latinAddress.length() * 100 / fulladdress.length());

        return matchPercent < translationConfig.getThreshold();
    }

    private boolean isInowRelationAlreadyExists(InowProfileImpl inowProfile)
    {
        return getInowProfileRelationService().isRelationExists(inowProfile.getSid());
    }

    private TranslationConfguration getTranslationConfig()
    {
        ServiceContext configContext = new ServiceContext(null, null);

        try
        {
            return (TranslationConfguration) getRequiredConfiguration(configContext);
        }
        catch (ConfigurationException e)
        {
            throw new IllegalStateException(e);
        }
    }

    public AmazonSNSClientImpl getAwsSnsClient()
    {
        return awsSnsClient;
    }

    public void setAwsSnsClient(AmazonSNSClientImpl awsSnsClient)
    {
        this.awsSnsClient = awsSnsClient;
    }

    public String getAwsSnsTopic()
    {
        return awsSnsTopic;
    }

    public void setAwsSnsTopic(String awsSnsTopic)
    {
        this.awsSnsTopic = awsSnsTopic;
    }

    public InowProfileDao getInowProfileDao()
    {
        return inowProfileDao;
    }

    public void setInowProfileDao(InowProfileDao inowProfileDao)
    {
        this.inowProfileDao = inowProfileDao;
    }

    public InowProfileRelationService getInowProfileRelationService()
    {
        return inowProfileRelationService;
    }

    public void setInowProfileRelationService(InowProfileRelationService inowProfileRelationService)
    {
        this.inowProfileRelationService = inowProfileRelationService;
    }

}
-- Adding new attribute named status to translation orphans table
ALTER TABLE TRANSLATION_ORPHANS
ADD STATUS VARCHAR2(20) DEFAULT 'PENDING' NOT NULL;

COMMIT;

/
package com.modeln.cdm.service.translation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.infonow.framework.util.spring.JdbcTemplateProxy;
import com.infonow.framework.util.spring.NamedParameterJdbcTemplateProxy;
import com.infonow.service.recovery.RecoveryQueries;
import com.infonow.service.recovery.RecoveryQueriesImpl;
import com.infonow.service.recovery.controller.TranslationController;
import org.json.JSONException;
import org.json.JSONObject;

import com.infonow.crux.dao.InowProfileDao;
import com.infonow.crux.impl.InowProfileImpl;
import com.infonow.framework.service.ServiceResponse;
import com.infonow.framework.service.configuration.support.ServiceDefinition;
import com.infonow.framework.service.context.ServiceContext;
import com.infonow.framework.service.support.ServiceException;
import com.infonow.service.configuration.ConfigurationException;
import com.infonow.service.configuration.impl.AbstractBaseServiceWithConfiguration;
import com.infonow.service.recovery.TranslationOrphan;
import com.modeln.cdm.integration.aws.AmazonSNSClientImpl;
import com.modeln.cdm.service.translation.configuration.TranslationConfguration;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import software.amazon.awssdk.utils.StringUtils;

import javax.sql.DataSource;

@ServiceDefinition(name = "translationService", abstraction = com.modeln.cdm.service.translation.TranslationService.class, implementation = com.modeln.cdm.service.translation.TranslationServiceImpl.class)
public class TranslationServiceImpl extends AbstractBaseServiceWithConfiguration implements TranslationService
{
    private InowProfileRelationService inowProfileRelationService;

    private RecoveryQueries _recoveryQueries;

    private AmazonSNSClientImpl awsSnsClient;

    private String awsSnsTopic;

    private InowProfileDao inowProfileDao;

    private static String UNWANTED_CHARS;

    private static String LATIN_CHARS;

    private static Pattern UNWANTED_PATTERN;

    private static Pattern LATIN_PATTERN;

    private TranslationConfguration translationConfig;

    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    private JdbcTemplate template;


    private TranslationController translationController;

    Matcher matcher;

    public TranslationServiceImpl()
    {
        if (UNWANTED_CHARS == null)
        {
            UNWANTED_CHARS = "[\\u0020-\\u002F\\u003A-\\u0040\\u005B-\\u0060\\u007B-\\u007E\\u0030-\\u0039\\u00A0-\\u00BF\\u2013-\\u204A]";
        }

        if (LATIN_CHARS == null)
        {
            LATIN_CHARS = "[\\u0041-\\u005A\\u0061-\\u007A\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u00FF\\uFF00-\\uFF60]+";
        }

        if (UNWANTED_PATTERN == null)
        {
            UNWANTED_PATTERN = Pattern.compile(UNWANTED_CHARS);
        }

        if (LATIN_PATTERN == null)
        {
            LATIN_PATTERN = Pattern.compile(LATIN_CHARS);
        }
    }

    public RecoveryQueries getRecoveryQueries()
    {
        if (_recoveryQueries == null)
        {
            throw new IllegalStateException("The attribute _recoveryQueries can not be null");
        }
        return _recoveryQueries;
    }
//    public RecoveryQueries getRecoveryQueries()
//    {
//        return recoveryQueries;
//    }

    public void setRecoveryQueries(RecoveryQueries recoveryQueries)
    {
        _recoveryQueries = recoveryQueries;
    }
    public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate()
    {
        return this.namedParameterJdbcTemplate;
    }

    @SuppressWarnings("unchecked")
    @Override
    public ServiceResponse execute(ServiceContext serviceContext, Object userObject, Object userData)
            throws ServiceException
    {
        if (!(userData instanceof List<?>))
        {
            throw new IllegalArgumentException("Argument userData must be a List but was " + userData == null ? null
                    : userData.getClass().getName());
        }

        System.out.println("userdata : ");
        System.out.println(userData);
        List<TranslationOrphan> ids = ((List<TranslationOrphan>) userData);
        List<JSONObject> addrs = new ArrayList<JSONObject>();
        translationConfig = getTranslationConfig();

        String sql = "update translation_orphans set status = :status where sid = :sid";
        List<Long> translationIds = new ArrayList<Long>();
        for (TranslationOrphan id : ids)
        {
            Long sid = id.getSid();
//            Map<String, Object> paramMap = new HashMap<String, Object>();
//            paramMap.put("sid", sid);
//            paramMap.put("status","IN PROGRESS");
            InowProfileImpl inowProfile = (InowProfileImpl) getInowProfileDao().getBySid(id.getInowProfileSid());

            boolean entityNeedsTranslation = isNonLatin(inowProfile.getName());

            String address = inowProfile.getStreet1() + inowProfile.getStreet2() + inowProfile.getCity()
                    + inowProfile.getStateProvince() + inowProfile.getPostalCode();
            boolean addressNeedsTranslation = isNonLatin(address);//The method returns true if the percentage of Latin characters in the cleaned addressComponent is below the threshold specified in translationConfig.

            //entity needs translation and address needs translation both are false => addr turns out to be null
            JSONObject addr = createTranslationInput(inowProfile, entityNeedsTranslation, addressNeedsTranslation);
//            if(getNamedParameterJdbcTemplate() != null)
//            {
//                getNamedParameterJdbcTemplate().update(sql, paramMap);
//            }

            translationIds.add(sid);//just trying it out
            if (addr != null)
            {
                translationIds.add(sid);
                addrs.add(addr);
                ids.remove(id);//since the ids which are in pending state must not be deleted
            }

        }
//        getRecoveryQueries().test();//is it because of getRecoveryQueries
        changeStatus("IN PROGRESS",translationIds);//just trying it out
        if (!addrs.isEmpty())//addrs list is empty, when does it enter into this condition??
        {
            publish(addrs);
            changeStatus("IN PROGRESS",translationIds);
            //call a method, pass translation orphans ids list
        }

        return new ServiceResponse("Success", ServiceResponse.SUCCESS);
    }

    public void changeStatus(String status,List<Long> translationIds){
        getRecoveryQueries().updateStatusTranslationOrphans(status, translationIds);
    }

    protected JSONObject createTranslationInput(InowProfileImpl inowProfile, boolean entityNeedsTranslation,
            boolean addressNeedsTranslation) throws ServiceException
    {
            JSONObject addr = null;

        if ((entityNeedsTranslation || addressNeedsTranslation)
                && isTranslationNeededForCountry(inowProfile.getCountryObject().getTwoCharCode()) && !isInowRelationAlreadyExists(inowProfile))
        {
            addr = new JSONObject();
            //pick the orphans which are in pending state
            /*Fetch the pending orphans.
            Update their status to IN PROGRESS.
            Save the updated status back to the database.
             */

//            loadIds(10);


//            //change the value of the attribute status in the table translation_orphans from pending to in progress
//            TranslationController translationController = new TranslationController();
//            List<TranslationOrphan> pendingOrphans = translationController.getPendingOrphans();//these orphans must be sent to the amazon SQS and the status of those orphans must be changed to in progress
//            translationController.updateOrphanStatus(pendingOrphans, "IN PROGRESS");//is this where im supposed to change the
            //status of the orphan, I dont think so since im updating the status of all the orphans which are in pending
            //state but I'll only have to update the status of the orphan which is picked up AWS queue(SQS)

            System.out.println("inow id : "+inowProfile.getId());

            try
            {
                addr.put("id", inowProfile.getIdentifier());

                JSONObject addrDetails = new JSONObject();

                if (entityNeedsTranslation)
                {
                    if (!StringUtils.isEmpty(inowProfile.getName()))
                    {
                        addrDetails.put("name", inowProfile.getName());
                    }
                }
                if (addressNeedsTranslation)
                {
                    if (!StringUtils.isEmpty(inowProfile.getStreet1()))
                    {
                        addrDetails.put("street1", inowProfile.getStreet1());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getStreet2()))
                    {
                        addrDetails.put("street2", inowProfile.getStreet2());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getCity()))
                    {
                        addrDetails.put("city", inowProfile.getCity());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getStateProvince()))
                    {
                        addrDetails.put("stateprovince", inowProfile.getStateProvince());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getPostalCode()))
                    {
                        addrDetails.put("postalcode", inowProfile.getPostalCode());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getCountry()))
                    {
                        addrDetails.put("country", inowProfile.getCountryObject().getTwoCharCode());
                    }
                }

                addr.put("query", addrDetails);
                addr.put("target", "EN");
            }
            catch (JSONException e)
            {
                throw new ServiceException("Exception while converting InowProfile to JSONObject" + e);
            }
        }

        return addr;

    }

    public void publish(List<JSONObject> addrs) throws ServiceException
    {
        //here change the orphan's status to in progress

        try
        {
            getAwsSnsClient().publishSingleMessages(getAwsSnsTopic(), addrs);
        }
        catch (Exception e)
        {
            throw new ServiceException("Exception while publishing message to Amazon SNS", e);
        }
    }

    private boolean isTranslationNeededForCountry(String country)
    {
        return translationConfig.getCountries().contains(country);
    }

    private boolean isNonLatin(String addressComponent)
    {
        addressComponent = addressComponent.replace("null", "");

        matcher = UNWANTED_PATTERN.matcher(addressComponent);
        String fulladdress = matcher.replaceAll("");

        if (StringUtils.isEmpty(fulladdress))
        {
            return false;
        }

        String latinAddress = "";

        matcher = LATIN_PATTERN.matcher(fulladdress);
        while (matcher.find())
        {
            latinAddress += matcher.group();
        }

        float matchPercent = (latinAddress.length() * 100 / fulladdress.length());

        return matchPercent < translationConfig.getThreshold();
    }

    private boolean isInowRelationAlreadyExists(InowProfileImpl inowProfile)
    {
        return getInowProfileRelationService().isRelationExists(inowProfile.getSid());
    }

    private TranslationConfguration getTranslationConfig()
    {
        ServiceContext configContext = new ServiceContext(null, null);

        try
        {
            return (TranslationConfguration) getRequiredConfiguration(configContext);
        }
        catch (ConfigurationException e)
        {
            throw new IllegalStateException(e);
        }
    }

    public AmazonSNSClientImpl getAwsSnsClient()
    {
        return awsSnsClient;
    }

    public void setAwsSnsClient(AmazonSNSClientImpl awsSnsClient)
    {
        this.awsSnsClient = awsSnsClient;
    }

    public String getAwsSnsTopic()
    {
        return awsSnsTopic;
    }

    public void setAwsSnsTopic(String awsSnsTopic)
    {
        this.awsSnsTopic = awsSnsTopic;
    }

    public InowProfileDao getInowProfileDao()
    {
        return inowProfileDao;
    }

    public void setInowProfileDao(InowProfileDao inowProfileDao)
    {
        this.inowProfileDao = inowProfileDao;
    }

    public InowProfileRelationService getInowProfileRelationService()
    {
        return inowProfileRelationService;
    }

    public void setInowProfileRelationService(InowProfileRelationService inowProfileRelationService)
    {
        this.inowProfileRelationService = inowProfileRelationService;
    }

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>
	<import resource="commonService.xml" />

	<!-- Start of Locator service definition -->
	<bean id="serviceLocatorService"
		class="com.infonow.framework.service.remoting.local.ServiceAccessor">
		<property name="serviceInterface"
			value="com.infonow.framework.service.locator.ServiceLocatorService" />
	</bean>

	<!-- Start of TranslationService definition -->
	<bean id="translationService"
		class="com.modeln.cdm.service.translation.TranslationServiceImpl">

		<property name="serviceLocatorService"
			ref="serviceLocatorService" />

		<property name="configurationServiceUrl"
			value="local://localhost/configurationService" />
		<property name="inowProfileRelationService"
			ref="inowProfileRelationService" />
		<property name="awsSnsClient" ref="awsSnsClient" />
		<property name="inowProfileDao" ref="inowProfileDao" />
		<property name="mappingFileName"
			value="translationMapping.xml" />
		<property name="awsSnsTopic"
			value="${translation.aws.sns.topicArn}" />
		<property name="recoveryQueries"
				  ref="recoveryQueries" />
	</bean>

	<bean id="recoveryQueries" class="com.infonow.service.recovery.RecoveryQueriesImpl">
		<property name="template" ref="nucleusJdbcTemplate"/>
		<property name="namedParameterJdbcTemplate" ref="nucleusNamedParameterJdbcTemplate"/>
	</bean>
    <bean id="inowProfileRelationService"
    class="com.infonow.framework.service.locator.impl.ServiceLocatorServiceProxyFactory">        
        <property name="serviceUrl" value="local://localhost/relationService" />        
        <property name="serviceLocatorService" ref="serviceLocatorService" />        
        <property name="className" value="com.modeln.cdm.service.relation.InowProfileRelationServiceImpl" />    
    </bean>
 
	<bean id="awsSnsClient"
		class="com.modeln.cdm.integration.aws.AmazonSNSClientImpl"
		init-method="startup" destroy-method="shutdown">

		<property name="awsCredentials" ref="awsCredentials" />
	</bean>
	<bean id="inowProfileRelationDao"
		class="com.infonow.crux.dao.orm.hibernate.InowProfileRelationDaoImpl">
		<property name="sessionFactory" ref="nucleusSessionFactory" />
	</bean>

	<bean id="inowProfileDao"
		class="com.infonow.crux.dao.orm.hibernate.InowProfileDao">
		<property name="sessionFactory" ref="nucleusSessionFactory" />
	</bean>

</beans>

package com.infonow.service.recovery.controller;

import java.util.Iterator;
import java.util.List;

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.infonow.framework.logging.Level;
import com.infonow.framework.logging.Logger;
import com.infonow.framework.service.context.ServiceContext;
import com.infonow.service.recovery.AbstractRunner;
import com.infonow.service.recovery.TranslationOrphan;

import static org.apache.commons.collections4.CollectionUtils.collect;

public class TranslationController extends ServiceCallerBatchController<TranslationOrphan>
{
    private static final String NAME = "Translation";

    public TranslationController()
    {
        super(NAME);
    }

    @Override
    public List<TranslationOrphan> loadIds(int numToRead)
    {
        List<TranslationOrphan> relationOrphans = getRecoveryQueries().getTranslationOrphans(numToRead,"PENDING");//new attribute here named status, gets all the orphans which are in pending state
        //TranslationController will load orphans that are in PENDING(or NULL for backward compatibility) status to be processed by TranslationService, when does the entry in the table happen?
        return relationOrphans;
    }

    public void changeStatus()
    {
        List<TranslationOrphan> relationOrphans = loadIds(10);

    }

    @Override
    protected RecoveryRunner<List<TranslationOrphan>, ?> createRunner(List<TranslationOrphan> ids)
    {
        return new InowRelationRunner(ids);
    }

    @Override
    protected void cleanUp(RecoveryRunner<List<TranslationOrphan>, ?> runner)
    {
        List<TranslationOrphan> ids = runner.getId();//skip the orphans which are in pending or inprogress state
        //loop over ids, create a new list of ids without inprogress and pending status
        System.out.println("ids");
        System.out.println(ids);
        getRecoveryQueries().deleteTranslateOrphans(ids);
    }

    @Override
    public int getNumberOfIdsPerBatch()
    {
        return getNumberOfIdsToRead();
    }

    /**
     * The Runnable instance use to process a batch of staging Addresses
     */
    private class InowRelationRunner extends AbstractRunner<List<TranslationOrphan>, Object>
    {
        ServiceContext _context = null;

        private Logger LOG = Logger.getLogger(TranslationOrphan.class.getName());

        InowRelationRunner(List<TranslationOrphan> ids)
        {
            super(ids, NAME, getService(), getTransactionTemplate());
            _context = new ServiceContext();
            _context.setContextType("default");
        }

        @Override
        public Object loadDataObject(List<TranslationOrphan> ids)
        {
            return loadMatchType(ids);
        }

        @Override
        public Object loadUserObject(List<TranslationOrphan> ids)
        {
            // There is a bug in AbstractRunner where the userObject and userData are swapped.
            // I don't want to change it because it might break every service, so I'll just
            // flip the objects here like the other services seem to do.
            // This is actually the userData in the service.
            return ids;
        }

        @Override
        public String loadMatchType(List<TranslationOrphan> ids)
        {
            return null;
        }

        @Override
        public ServiceContext createServiceContext(Object dataObject)
        {
            return _context;
        }

        @Override
        protected void handleRunException(Exception exception)
        {
            // We are going to make a new orphan with an incremented retry count
            // and put it back in the queue.
            List<TranslationOrphan> oldOrphans = (List<TranslationOrphan>) this.getId();

            for (Iterator<TranslationOrphan> i = oldOrphans.iterator(); i.hasNext();)
            {
                TranslationOrphan oldOrphan = i.next();

                long newRetryCount = oldOrphan.getRetryCount() + 1;

                // First, compute the severity of the exception to be logged based on the retryCount.
                Level loggingLevel = Level.WARNING;

                // We only make it SEVERE if the retryCount is a multiple of 10
                // to avoid hammering engineering support with e-mails.
                if ((newRetryCount % 10) == 0)
                {
                    loggingLevel = Level.SEVERE;
                }

                LOG.log(loggingLevel,
                        getDescription() + ":  Error processing id - " + getId().toString() + ", re-submitting orphan.",
                        exception);

                PlatformTransactionManager txnMgr = null;
                TransactionStatus status = null;

                try
                {
                    txnMgr = _transactionTemplate.getTransactionManager();
                    status = txnMgr.getTransaction(
                            new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW));

                    double delayMinutesAsDouble = 0.0;
                    int delayMinutesAsInteger = 0;

                    // Compute the delay minutes for the new orphan.
                    // Since there is the potential for numeric overflows,
                    // we are wrapping the math in a try/catch block.
                    try
                    {
                        delayMinutesAsDouble = Math.pow(2, newRetryCount);
                        delayMinutesAsInteger = (int) delayMinutesAsDouble;
                    }
                    catch (Exception e)
                    {
                        delayMinutesAsInteger = 7 * 24 * 60; // Push re-try out to 7 days if numeric overflows occur.
                    }

                    getOrchestrationDao().insertTranslationOrphan(delayMinutesAsInteger, oldOrphan.getInowProfileSid(),
                            newRetryCount);

                    try
                    {
                        if (!status.isRollbackOnly())
                        {
                            txnMgr.commit(status);
                        }
                    }
                    catch (Exception e)
                    {
                        String msg = getDescription() + ":  Error processing id - " + getId().toString();
                        LOG.log(Level.SEVERE, msg, e);
                    }
                }
                catch (Exception e)
                {
                    String msg = getDescription() + ":  Error processing id - " + getId().toString();
                    LOG.log(Level.SEVERE, msg, e);
                }
                finally
                {
                    try
                    {
                        if (txnMgr != null && status != null && !status.isCompleted())
                        {
                            txnMgr.rollback(status);
                        }
                    }
                    catch (Exception ee)
                    {
                        String msg = getDescription() + ":  Error processing id - " + getId().toString();
                        LOG.log(Level.SEVERE, msg, ee);
                    }

                }
            }
        }

    } // end inner-class InowRelationRunner

}
/**
 * RecoveryQueriesImpl.java $Revision$
 *
 * Copyright (c) 2005-2008 InfoNow Corporation. All rights reserved.
 * INFONOW PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.infonow.service.recovery;

import com.infonow.crux.DataFile;
import com.infonow.crux.composite.CompositeAccountClassificationOrphan;
import com.infonow.crux.dao.jdbc.mapper.GenericMapper;
import com.infonow.crux.dao.jdbc.mapper.GenericRowMapper;
import com.infonow.crux.dataFile.DataFileOrphanId;
import com.infonow.crux.learning.LearnedName;
import com.infonow.crux.learning.LearnedName.ApprovalStatus;
import com.infonow.crux.learning.LearnedNameOrphan;
import com.infonow.crux.schedule.submissionSchedule.SubmissionScheduleOrphan;
import com.infonow.crux.sqlGeneration.SqlGenerationUtils;
import com.infonow.crux.util.CollectionUtils;
import com.infonow.crux.util.Constants.DataAddressTypeEnum;
import com.infonow.framework.logging.Logger;
import com.infonow.framework.util.spring.JdbcTemplateProxy;
import com.infonow.framework.util.spring.NamedParameterJdbcTemplateProxy;
import com.infonow.service.submissionPeriodUpdate.SubmissionPeriodUpdateOrphanId;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.transaction.annotation.Transactional;


/**
 * Query implementations for the recovery service.
 * 
 * @author Chris Lambrecht
 * 
 * @version $Revision$
 */
@SuppressWarnings("rawtypes")
public class RecoveryQueriesImpl implements RecoveryQueries
{
    private static final Logger LOG = Logger.getLogger(RecoveryQueriesImpl.class.getName());
    private static final String DELETE_RL_ORPHAN = "delete from rl_orphans where master_run_id = ?";
    private final RowMapper SID_MAPPER = new SidRowMapper();
    private final RowMapper LINE_ITEM_MAPPER = new LineItemRowMapper();
    private final RowMapper VALIDATION_ITEM_MAPPER = new ValidationRowMapper();
    private final RowMapper SERIAL_NUMBER_MAPPER = new SerialNumberRowMapper();
    private final RowMapper TUPPLE_MAPPER = new TupleMapper();
    private final RowMapper SLIA_MAPPER = new SliaProfileMapper();
    private final RowMapper INOW_PROFILE_HIERARCHY_MAPPER = new InowProfileHierarchyRowMapper();
    private final RowMapper HIERARCHY_PARENT_MAPPER = new HierarchyParentMapper();
    private final RowMapper ADDRESS_HISTORY_MAPPER = new AddressHistoryMapper();
    private final RowMapper INOW_PROFILE_MAPPER = new InowProfileRowMapper();
    private final RowMapper SALES_LINE_ITEM_NORMALIZATION_MAPPER = new SalesLineItemNormalizationRowMapper();
    private final RowMapper REPORTING_PARTNER_MAPPER = new ReportingPartnerRowMapper();
    private final RowMapper PROFILE_PARTNER_MAPPER = new ProfilePartnerRowMapper();
    private final RowMapper EXPORTER_ORPHAN_MAPPER = new ExporterOrphanRowMapper();
    private final RowMapper COMPOSITE_ACCOUNT_CLASS_ORPHAN_MAPPER = new CompositeAccountClassificationOrphanMapper();
    private final RowMapper SUBMISSION_SCHEDULE_ORPHAN_MAPPER = new SubmissionScheduleOrphanMapper();
    private final RowMapper LEARNED_NAME_ORPHAN_MAPPER = new LearnedNameOrphanMapper();
    private final RowMapper INOW_STANDARDIZATION_ORPHAN_MAPPER = new InowStandardizationOrphanMapper();
    private final RowMapper INOW_TRANSLATION_ORPHAN_MAPPER = new InowTranslationOrphanMapper();
    private final RowMapper SUBMISSION_SCHEDULE_UPDATE_ORPHAN_MAPPER = new SubmissionPeriodUpdateOrphanMapper();
    private final RowMapper INOW_RELATION_ORPHAN_MAPPER = new InowRelationOrphanMapper();
    private final RowMapper EXPORT_OUTPUT_ORPHAN_MAPPER = new ExportOutputOrphanMapper();
    private final RowMapper VALIDATION_TRACKING_ORPHAN_MAPPER = new ValidationTrackingRowMapper();
    private final RowMapper VALIDATION_DELETED_ORPHAN_MAPPER = new ValidationDeletedRowMapper();

    @Autowired
    private JdbcTemplate template;
    @Autowired
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    /**
     * Default Constructor
     */
    public RecoveryQueriesImpl()
    {
        super();
    }

    /**
     * Get the value of the template field.
     * 
     * @return Returns the value of template.
     */
    public JdbcTemplate getTemplate()
    {
        return this.template;
    }

    /**
     * Set the value of the template field.
     * 
     * @param template
     *            The template value to set.
     */
    public void setTemplate(JdbcTemplate template)
    {
        this.template = template;
    }

    /**
     * Get the value of the namedParameterJdbcTemplate field.
     * 
     * @return Returns the value of namedParameterJdbcTemplate.
     */
    public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate()
    {
        System.out.println("in getNamedParameterejdbctem");
        System.out.println(this);
        System.out.println(this.namedParameterJdbcTemplate);

        // Get the current stack trace
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();

        // stackTraceElements[0] is getStackTrace
        // stackTraceElements[1] is getNamedParameterJdbcTemplate
        // stackTraceElements[2] is the method that called getNamedParameterJdbcTemplate
        if (stackTraceElements.length > 2) {
            String callerMethodName = stackTraceElements[2].getMethodName();
            System.out.println("Caller Method: " + callerMethodName);
            System.out.println(this);
            System.out.println(this.namedParameterJdbcTemplate);
        }
        return this.namedParameterJdbcTemplate;
    }

    /**
     * Set the value of the namedParameterJdbcTemplate field.
     * 
     * @param namedParameterJdbcTemplate
     *            The namedParameterJdbcTemplate value to set.
     */
    public void setNamedParameterJdbcTemplate(NamedParameterJdbcTemplate namedParameterJdbcTemplate)
    {
        this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
    }

    /**
     * Inject a reference to the {@link DataSource} to use for all queries
     * 
     * @param dataSource
     *            - database connect source
     */
    public void setDataSource(DataSource dataSource)
    {
        template = new JdbcTemplateProxy(dataSource);
        namedParameterJdbcTemplate = new NamedParameterJdbcTemplateProxy(dataSource);
    }

    /**
     * Selects input file ids from the orphan table. Limit the number of rows returned to numIds and make sure that the
     * ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<Long> getParserOrphans(int numIds, int maxPerCustomer)
    {
        final String sql = "select data_file_sid from ( "
                + "select * from ( "
                + "select ifo.*, "
                + "       rank() over( partition by ifo.customer_sid order by sid ) rank_overall "
                + "from ( "
                + "select ifo.*, "
                + " rank() over( partition by ifo.customer_sid, ifo.type order by nvl(ifo.update_date,ifo.create_date) ) rank_of_type "
                + "from ( " + "select ifo.data_file_sid, " + "       if.customer_sid, " + "       ifo.sid, "
                + "       ifo.process_time, if.create_date, if.update_date, "
                + "       CASE WHEN (instr(upper(data_type),'CATALOG') > 0) THEN 'CATALOG' "
                + "            WHEN (instr(upper(data_type),'PROFILE') > 0) THEN 'PROFILE' "
                + "            ELSE data_type  " + "       END as type " + "from input_file_orphans ifo "
                + "join data_file if on ifo.data_file_sid = if.sid " + "where process_time < sysdate " + ") ifo "
                + ") ifo " + "where ifo.type not in ('CATALOG','PROFILE')  " + "      or ifo.rank_of_type = 1 " + ")  "
                + "where rank_overall <= ? " + "order by rank_overall " + ") where rownum < ? ";

        return getTemplate().query(sql, new Object[]
        { maxPerCustomer, numIds }, SID_MAPPER);
    }

    /**
     * Select sales product ids from the orphan table. Limit the number of rows returned to numIds and makes sure that
     * the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<LineItemOrphanId> getSalesProductOrphanIds(int numIds)
    {

        return getIds("sales_line_item_orphans", new String[]
        { "sid", "sales_line_item_sid", "match_type", "customer_sid" }, null, numIds, LINE_ITEM_MAPPER);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#getSalesLineItemSerialNumberOrphanIds(int,
     * com.infonow.service.recovery.SerialNumberOrphanId)
     */
    @SuppressWarnings("unchecked")
    public List<SerialNumberOrphanId> getSalesLineItemSerialNumberOrphanIds(int numIds)
    {
        return getIds("SLI_SERIAL_NUMBER_ORPHANS", new String[]
        { "sid", "sli_serial_number_sid", "match_type", "customer_sid" }, null, numIds, SERIAL_NUMBER_MAPPER);
    }

    /**
     * Select inv product ids from the orphan table. Limit the number of rows returned to numIds and makes sure that the
     * ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<LineItemOrphanId> getInvProductOrphanIds(int numIds)
    {
        return getIds("inv_line_item_orphans", new String[]
        { "sid", "inv_line_item_sid", "match_type", "customer_sid" }, null, numIds, LINE_ITEM_MAPPER);
    }

    /**
     * Select Profile Partner ids from the orphan table. Limit the number of rows returned to numIds and makes sure that
     * the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<ProfilePartnerOrphanId> getProfilePartnerOrphanIds(int numIds)
    {
        final String query = "SELECT" + 
                "    sid," + 
                "    profile_partner_sid," + 
                "    customer_sid," + 
                "    match_type," +
                "    retry_count" +
                " FROM" + 
                "    (" + 
                "        SELECT" + 
                "            *" + 
                "        FROM" + 
                "            (" + 
                "                SELECT" + 
                "                    sid," + 
                "                    profile_partner_sid," + 
                "                    customer_sid," + 
                "                    match_type," + 
                "                    retry_count," +
                "                    RANK() OVER(" + 
                "                        PARTITION BY customer_sid" + 
                "                        ORDER BY" + 
                "                            sid" + 
                "                    ) rank" + 
                "                FROM" + 
                "                    (" + 
                "                        SELECT" + 
                "                            po.sid," + 
                "                            po.profile_partner_sid," + 
                "                            pp.customer_sid," + 
                "                            po.match_type," + 
                "                            po.retry_count" + 
                "                        FROM" + 
                "                            profile_orphans po" + 
                "                            JOIN profile_partner pp ON pp.sid = po.profile_partner_sid" + 
                "                        WHERE" + 
                "                            process_time < SYSDATE AND (pp.address_sid is null or pp.address_sid = 0)" + 
                "                            " + 
                "                        UNION " + 
                "                        " + 
                "                         SELECT" + 
                "                            po.sid," + 
                "                            po.profile_partner_sid," + 
                "                            pp.customer_sid," + 
                "                            po.match_type," +
                "                            po.retry_count" +
                "                        FROM" + 
                "                            profile_orphans po" + 
                "                            JOIN profile_partner pp ON pp.sid = po.profile_partner_sid" + 
                "                            JOIN address_history ah ON ah.address_sid = pp.address_sid" + 
                "                        WHERE" + 
                "                            process_time < SYSDATE AND ah.stage = 'API_ENRICHED' " + 
                "                    )" + 
                "            )" + 
                "        ORDER BY" + 
                "            rank" + 
                "    )" + 
                "WHERE" + 
                "    ROWNUM < ?";

        return getTemplate().query(query, new Object[] { numIds }, PROFILE_PARTNER_MAPPER);
    }

    /**
     * Select Sales Record Address ids from the orphan table. Limit the number of rows returned to numIds and makes sure
     * that the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<SalesLineItemAddressOrphanId> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaProfileOrphanIds(int numIds)
    {
        final String query = "select * from (select * from (select sliapo.sid, "
                + "       sliapo.sales_line_item_sid, " + "       sliapo.address_Sid, " + "       sliapo.address_type, "
                + "       sliapo.match_type, sliapo.customer_sid, sli.BILL_TO_ADDR_EXTERNAL_ID"
                + "       ,sli.SOLD_TO_ADDR_EXTERNAL_ID, sli.SHIP_TO_ADDR_EXTERNAL_ID"
                + "       ,sli.SELL_FROM_ADDR_EXTERNAL_ID, sli.SHIP_FROM_ADDR_EXTERNAL_ID"
                + "       ,sli.SALES_IN_ADDR_EXTERNAL_ID, sli.PURCH_CUST_ADDR_EXTERNAL_ID"
                + "       ,sli.DER_END_CUST_ADDR_EXTERNAL_ID,sli.deleted "
                + "       ,case  when exists (select 1 from address_history ah where ah.address_sid = sliapo.address_sid and ah.stage = 'RAW')"
                + "               then 1 else 0 end raw_exists "
                + "       ,case  when exists (select 1 from address_history ah where ah.address_sid = sliapo.address_sid and ah.stage = 'REF_DATA_ENRICHED') "
                + "               then 1 else 0 end ref_data_exists,"
                + "       rank() over(partition by sliapo.customer_sid order by sliapo.sid) rank "
                + " from slia_profile_orphans sliapo join sales_line_item sli on sli.sid = sliapo.sales_line_item_sid and "
                + " sli.customer_sid = sliapo.customer_sid where process_time < sysdate"
                + " order by rank,address_sid) where (raw_exists = 0 or (raw_exists = 1 and ref_data_exists = 1))) where rownum < ? ";

        return getTemplate().query(query, new Object[] { numIds }, SLIA_MAPPER);

    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#getSliaCompositeAccountOrphanIds(int)
     */
    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaCompositeAccountOrphanIds(int numIds)
    {

        return getIds("slia_composite_acct_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "match_type", "customer_sid" }, null, numIds,
        TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<InowProfileHierarchyOrphanId> getInowProfileHierarchyOrphanIds(int numToRead)
    {

        final String query = "SELECT" + 
                "    * " + 
                " FROM" + 
                "    (" + 
                "        SELECT" + 
                "            *" + 
                "        FROM" + 
                "            (" + 
                "                SELECT" + 
                "                    ipho.sid," + 
                "                    ipho.inow_profile_sid," + 
                "                    ipho.customer_sid," + 
                "                    ipho.named_ip_hierarchy_sid," + 
                "                    ipho.reprocess_flag," + 
                "                    ipho.retry_count," + 
                "                    ipho.priority," + 
                "                    RANK() OVER(" + 
                "                        PARTITION BY ipho.customer_sid" + 
                "                        ORDER BY" + 
                "                            sid ASC" + 
                "                    ) rank," +
                "                    upper(csr.entity_name)" + 
                "                    || '-----'" + 
                "                    || csr.country orphan_key" + 
                "                FROM" + 
                "                    inow_profile_hierarchy_orphans   ipho" + 
                "                    LEFT JOIN csr_overlay_v                    csr ON csr.customer_sid = ipho.customer_sid" + 
                "                                              AND ipho.inow_profile_sid = csr.ip_sid" + 
                "                WHERE" + 
                "                    process_time < sysdate" + 
                "            )" + 
                "        ORDER BY" + 
                "            priority," + 
                "            rank" + 
                "    ) " + 
                " WHERE " + 
                "    ROWNUM < :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, INOW_PROFILE_HIERARCHY_MAPPER);

    }

    @SuppressWarnings("unchecked")
    public List<HierarchyParentOrphanId> getHierarchyParentOrphanIds(int numToRead)
    {

        final String query = "select * from ( " + "select * from ( " + "select hpo.sid, "
                + "       hpo.inow_profile_sid, " + "       hpo.customer_sid, "
                + "       hpo.named_ip_hierarchy_sid, " + "       hpo.reprocess_flag, " + "       hpo.retry_count, "
                + "       hpo.priority, "
                + "       rank() over( partition by hpo.customer_sid order by sid asc) rank "
                + "from hierarchy_parent_orphans hpo " + "where process_time < sysdate) "
                + "order by priority,rank " + ") " + "where rownum < :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, HIERARCHY_PARENT_MAPPER);

    }
    
    @SuppressWarnings("unchecked")
    public List<AddressHistoryOrphanId> getAddressHistoryOrphanIds(int numToRead, String tableName)
    {

        final String query = "select * from ( " + "select * from ( " + "select aho.sid, "
                + "       aho.address_history_sid, aho.retry_count "
                + "from " + tableName + " aho " + "where process_time < sysdate) "
                + "order by sid " + ") " + "where rownum <= :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, ADDRESS_HISTORY_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<SalesLineItemNormalizationOrphanId> getSalesLineItemNormalizationOrphanIds(int numToRead)
    {
        return getIds("sli_normalization_orphans", new String[]
        { "sid", "sales_line_item_sid", "customer_sid", "price_type, entity_sid" }, null, numToRead,
                SALES_LINE_ITEM_NORMALIZATION_MAPPER);
    }

    /**
     * Select Sales Record Address ids from the orphan table. Limit the number of rows returned to numIds and makes sure
     * that the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<SalesLineItemAddressOrphanId> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaReportingPartnerOrphanIds(int numIds)
    {
        return getIds("reporting_partner_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "null" }, null, numIds, TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationOrphanId> getSalesLineItemValidationOrphanIds(int numToRead)
    {

        final String query = "select * from ( " + "select sv.sid,sv.sales_line_item_sid,sv.match_type,sv.customer_sid, "
                + " sli.reporting_partner_sid,sli.data_file_sid,rank() over(partition by sv.customer_sid order by sv.create_date) rank_by_cust" 
                + " from sli_validation_orphans sv"
                + " join sales_line_item sli on sli.sid = sv.sales_line_item_sid "
                + " and sli.customer_sid = sli.customer_Sid where process_time < sysdate order by rank_by_cust,sv.create_date " 
                + ") where rownum <= :numToRead ";
        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, VALIDATION_ITEM_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaValidationOrphanIds(int numToRead)
    {
        return getIds("slia_validation_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "match_type", "customer_sid" }, null, numToRead,
        TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationTrackingOrphanId> getValidationTrackingOrphanIds(int numToRead)
    {
        return getIds("validation_tracking_orphans", new String[]
                        { "sid", "data_file_sid", "customer_sid",
                                "create_date", "update_date", "retry_count" }, null, numToRead,
                VALIDATION_TRACKING_ORPHAN_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationNotificationOrphanId> getValidationNotificationOrphanIds(int numToRead)
    {
        return getIds("validation_notification_orphans", new String[]
                        { "sid", "data_file_sid", "customer_sid",
                                "create_date", "update_date", "retry_count" }, null, numToRead,
                VALIDATION_DELETED_ORPHAN_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationOrphanId> getInventoryLineItemValidationOrphanIds(int numToRead)
    {
        final String query = "select * from ( " + "select sv.sid,sv.inv_line_item_sid,sv.match_type,sv.customer_sid, "
                + " ili.reporting_partner_sid,ili.data_file_sid,rank() over(partition by sv.customer_sid order by sv.create_date) rank_by_cust" 
                + " from ili_validation_orphans sv"
                + " join inv_line_item ili on ili.sid = sv.inv_line_item_sid "
                + " and ili.customer_sid = ili.customer_Sid where process_time < sysdate order by rank_by_cust,sv.create_date " 
                + ") where rownum <= :numToRead ";
        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, VALIDATION_ITEM_MAPPER);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#getReportingPartnerOrphanIds(int)
     */
    @SuppressWarnings("unchecked")
    @Override
    public List<ReportingPartnerOrphanId> getReportingPartnerOrphanIds(int numToRead)
    {
        return getIds("reporting_partner_inow_orphs", new String[]
        { "sid", "reporting_partner_sid" }, null, numToRead, REPORTING_PARTNER_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Long> getAddressStandardizationOrphans(int numToRead)
    {
        return getIds("addr_standardization_orphans", new String[]
        { "address_sid" }, null, numToRead, SID_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<InowStandardizationOrphanId> getInowProfileStandardizationOrphans(int numToRead)
    {
        return getIds("inow_profile_std_orphans", new String[]
        { "sid", "inow_profile_sid", "retry_count" }, null, numToRead, INOW_STANDARDIZATION_ORPHAN_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<InowProfileOrphanId> getInowProfileDedupOrphans(int numToRead)
    {
        return getIds("inow_profile_dedup_orphans", new String[]
        { "sid", "inow_profile_sid", "customer_sid" }, null, numToRead, INOW_PROFILE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Long> getProfilePartnerRematchOrphanIds(int numToRead)
    {
        return getIds("profile_rematch_orphans", new String[]
        { "profile_partner_sid" }, null, numToRead, SID_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<SalesLineItemAddressOrphanId> getSliaRematchOrphanIds(int numToRead)
    {
        return getIds("slia_profile_rematch_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "match_type" }, null, numToRead, TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<ExporterOrphanId> getExporterOrphanIds(int numToRead)
    {
        return getIds("exporter_orphans", new String[]
        { "sid", "customer_sid", "export_name" }, null, numToRead, EXPORTER_ORPHAN_MAPPER);
    }

    /**
     * Deletes the specified orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteDataFileId(Long id)
    {
        if (id == null)
        {
            return;
        }

        deleteId("input_file_orphans", "data_file_sid", id);
    }

    /**
     * Deletes the specified orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSalesProductId(LineItemOrphanId id)
    {
        deleteId("sales_line_item_orphans", id.getSid());
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#deleteSalesLineItemSerialNumberId(com.infonow.service.recovery.
     * SerialNumberOrphanId)
     */
    public void deleteSalesLineItemSerialNumberId(SerialNumberOrphanId id)
    {
        deleteId("sli_serial_number_orphans", id.getSid());
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteProfilePartnerId(ProfilePartnerOrphanId id)
    {
        deleteId("profile_orphans", "sid", id.getSid());
    }

    /**
     * Deletes the specified orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteInvProductId(LineItemOrphanId id)
    {
        deleteId("inv_line_item_orphans", id.getSid());
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSliaProfileId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_profile_orphans", id.getSid());
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#deleteSliaCompositeAccountId(com.infonow.service.recovery.
     * SalesLineItemAddressOrphanId)
     */
    public void deleteSliaCompositeAccountId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_composite_acct_orphans", id.getSid());
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSliaReportingPartnerId(SalesLineItemAddressOrphanId id)
    {
        deleteId("reporting_partner_orphans", id.getSid());
    }

    public void deleteSalesLineItemValidationId(List<ValidationOrphanId> ids)
    {
        String sql = new String("delete from sli_validation_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    public void deleteValidationTrackingIds(List<ValidationTrackingOrphanId> ids)
    {
        String sql = new String("delete from validation_tracking_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    public void deleteValidationNotificationIds(List<ValidationNotificationOrphanId> ids)
    {
        String sql = new String("delete from validation_notification_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    public void deleteSliaValidationId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_validation_orphans", id.getSid());
    }

    public void deleteInventoryLineItemValidationId(List<ValidationOrphanId> ids)
    {
        String sql = new String("delete from ili_validation_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteSalesLineItemOrphans(List<Long> salesLineItemSids)
    {
        String sql = new String("delete from sales_line_item_orphans where sales_line_item_sid in (:sids)");

        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", salesLineItemSids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteSalesLineItemOrphans(DataFile dataFile)
    {
        String sql = new String("delete from sales_line_item_orphans where sales_line_item_sid in "
                + " (select sid from sales_line_item where data_file_sid = ? and customer_sid = "
                + dataFile.getCustomer().getSid().toString() + ")");

        getTemplate().update(sql, new Object[]
        { dataFile.getSid() });
    }

    @Override
    public void deleteInvLineItemOrphans(List<Long> invLineItemSids)
    {
        String sql = new String("delete from inv_line_item_orphans where inv_line_item_sid in (:sids)");
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", invLineItemSids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Transactional
    @Override
    public void updateStatusTranslationOrphans(String status, List<Long> translationOrphanSids)//pass the array of sids, and pass status
    {
        String sql = new String("update translation_orphans set status = :status where sid in (:sids)");
        String commit = new String("commit");
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("sids", translationOrphanSids);
        paramMap.put("status", status);
        System.out.println("getNamedParameterJdbcTemplate() in update status method: "+getNamedParameterJdbcTemplate());
        System.out.println(getNamedParameterJdbcTemplate());
        if(getNamedParameterJdbcTemplate() != null || getTemplate() != null)//why are these null?
        {
            int rowsUpdated = getNamedParameterJdbcTemplate().update(sql, paramMap);
            System.out.println("Number of rows updated: " + rowsUpdated);
            getNamedParameterJdbcTemplate().query(commit,SID_MAPPER);
        }
        else{
            System.out.println("printing getNamedParameterJdbcTemplate : "+getNamedParameterJdbcTemplate());//why is this null???
            System.out.println("printing getTemplate : "+getTemplate());
            System.out.println("getNamedParameterJdbcTemplate is null");
        }
    }

    @Override
    public void test()//pass the array of sids, and pass status
    {

        String sql = new String("select sid from translation_orphans where sid = :sid");

        System.out.println("getNamedParameterJdbcTemplate() in test method: "+getNamedParameterJdbcTemplate());
        System.out.println(getNamedParameterJdbcTemplate());
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("sid", 2);
        System.out.println("getNamedParameterJdbcTemplate from test method : "+getNamedParameterJdbcTemplate());
        if(getNamedParameterJdbcTemplate() != null || getTemplate() != null)//why are these null?
        {
            getTemplate().update(sql,paramMap);
            getNamedParameterJdbcTemplate().update(sql, paramMap);
        }
        else{
            System.out.println("printing getNamedParameterJdbcTemplate : "+getNamedParameterJdbcTemplate());//why is this null???
            System.out.println("printing getTemplate : "+getTemplate());
            System.out.println("getNamedParameterJdbcTemplate is null");
        }
    }
    
    public void deleteRegexStandardizationOrphans(List<AddressHistoryOrphanId> orphanIds)//follow this
    {
        String sql = new String("delete from regex_std_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }
    
    @Override
    public void deleteRefDataStandardizationOrphans(List<AddressHistoryOrphanId> orphanIds)
    {
        String sql = new String("delete from ref_data_std_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }
    
    @Override
    public void deleteAddressHistoryOrphans(List<AddressHistoryOrphanId> orphanIds, String orphanTable)
    {
        String sql = new String("delete from " + orphanTable + " where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }
    
    @Override
    public void deleteAddressGradingOrphans(List<AddressHistoryOrphanId> orphanIds)
    {
        String sql = new String("delete from address_grading_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteTranslateOrphans(List<TranslationOrphan> orphanIds)
    {
        for (List<TranslationOrphan> orphans : CollectionUtils.partition(orphanIds, 1000))
        {
            String sql = new String(
                "delete from translation_orphans where sid in (:sids)");
            List<Long> sids = new ArrayList<>();
            orphans.forEach(ad -> sids.add(ad.getSid()));
            Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
            paramMap.put("sids", sids);

            getNamedParameterJdbcTemplate().update(sql, paramMap);
        }
    }

    @Override
    public void deleteExportOutputOrphans(List<ExportOutputOrphanId> orphanIds)
    {

        String sql = new String(
            "delete from export_output_orphans where sid in (:sids)");
        Map<String, Object> paramMap = new HashMap<String, Object>();
        List<Long> sids = orphanIds.stream().map(o -> o.getSid())
            .collect(Collectors.toList());
        List<List<Long>> sidsPartition = CollectionUtils.partition(sids, 1000);
        for (List<Long> batch : sidsPartition)
        {
            paramMap.put("sids", sids);

            getNamedParameterJdbcTemplate().update(sql, paramMap);
        }
    }

    @Override
    public void deleteSubmissionPeriodUpdateOrphans(
        SubmissionPeriodUpdateOrphanId orphanId)
    {

        String sql = new String(
            "delete from SP_UPDATE_ORPHANS where sid in (:sid)");
        Map<String, Long> paramMap = new HashMap<String, Long>();
        paramMap.put("sid", orphanId.getSid());

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteInowRelationOrphan(InowRelationOrphan orphanId)
    {
        String sql = new String("delete from relation_orphans where sid = :sid");
        Map<String, Long> paramMap = new HashMap<String, Long>();
        paramMap.put("sid", orphanId.getSid());

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteAddressQualityOrphans(List<AddressHistoryOrphanId> orphanIds)
    {
        String sql = new String("delete from address_quality_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteInvLineItemOrphans(DataFile dataFile)
    {
        String sql = new String("delete from inv_line_item_orphans where inv_line_item_sid in "
                + " (select sid from inv_line_item where data_file_sid = ? and customer_sid = "
                + dataFile.getCustomer().getSid().toString() + ")");

        getTemplate().update(sql, new Object[]
        { dataFile.getSid() });
    }

    @Override
    public List<DataFileOrphanId> getDataFileDeleteOrphans(int numToRead)
    {
        List<?> orphanIds = null;

        orphanIds = getIds("data_file_delete_orphans", new String[]
        { "sid", "data_file_sid", "customer_sid", "reprocess", "user_login", "retry_count", "preserve_date" }, null, null, numToRead, new GenericRowMapper()
        {
            @Override
            public Object mapRow(ResultSet rs, int arg1) throws SQLException
            {

                DataFileOrphanId dfoi = new DataFileOrphanId();
                dfoi.setDataFileSid(this.getLong(rs, "data_file_sid"));
                dfoi.setCustomerSid(this.getLong(rs, "customer_sid"));
                dfoi.setReprocess(this.getBooleanValue(rs, "reprocess"));
                dfoi.setUserLogin(this.getString(rs, "user_login"));
                dfoi.setSid(this.getLong(rs, "sid"));
                dfoi.setRetryCount(this.getLong(rs, "retry_count"));
                dfoi.setPreserveDate(this.getBooleanValue(rs, "preserve_date"));
                return dfoi;
            }

        });

        return (List<DataFileOrphanId>) orphanIds;
    }

    @SuppressWarnings("unchecked")
    public List<TranslationOrphan> getTranslationOrphans(int numToRead, String status)
    {

        String query = "select * from ( select ipro.sid, ipro.inow_profile_sid, ipro.retry_count, ipro.status, rownum cnt "
                + "from translation_orphans ipro where process_time < sysdate "
                + "order by sid ) result where rownum <= :numToRead ";


        Map<String, String> paramMap = new HashMap<String, String>();
        paramMap.put("numToRead", String.valueOf(numToRead));
        if(status!=null){
            query = query + "and result.status = :status";
            paramMap.put("status",status);
        }

        System.out.println("getNamedParameterJdbcTemplate() in method getTranslationOrphans : "+getNamedParameterJdbcTemplate());
        System.out.println(getNamedParameterJdbcTemplate());

        return getNamedParameterJdbcTemplate().query(query, paramMap, INOW_TRANSLATION_ORPHAN_MAPPER);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<SubmissionPeriodUpdateOrphanId> getSubmissionPeriodUpdateOrphans(int numToRead)
    {

        final String query =
            "select * from ( select spuo.sid, spuo.customer_sid, spuo.old_start_date, spuo.retry_count, rownum cnt "
                + "from sp_update_orphans spuo where process_time < sysdate "
                + "order by sid ) where rownum <= :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap,
            SUBMISSION_SCHEDULE_UPDATE_ORPHAN_MAPPER);
    }

    @Override
    public List<ExportOutputOrphanId> getExportOutputOrphans(int numToRead)
    {
        final String query = "WITH rowlimit AS ("
            + "SELECT * FROM ("
            + "SELECT EXPORT_STATUS_SID FROM export_output eo JOIN export_output_orphans eoo "
            + "ON eoo.EXPORT_OUTPUT_SID= eo.sid AND eo.customer_sid = eoo.customer_sid "
            + "WHERE eoo.process_time < sysdate + interval '10' second "
            + "GROUP BY EXPORT_STATUS_SID ) "
            + "WHERE rownum < :numToRead ) "
            + "SELECT eoo.SID,eoo.CUSTOMER_SID,eoo.SESSION_ID,eoo.EXPORT_OUTPUT_SID,"
            + "eo.EXPORT_STATUS_SID,eoo.retry_count,es.export_name, eoo.export_request_sid"
            + ",RANK() OVER (PARTITION BY eo.customer_sid,eo.EXPORT_STATUS_SID ORDER BY eoo.sid ) rankof "
            + "FROM EXPORT_OUTPUT eo JOIN EXPORT_OUTPUT_ORPHANS eoo ON eoo.EXPORT_OUTPUT_SID = eo.sid "
            + "JOIN rowlimit ON rowlimit.export_status_sid = eo.EXPORT_STATUS_SID "
            + "join export_status es on es.sid = eo.export_status_sid and es.customer_sid = eo.customer_sid "
            + "ORDER BY rankof";

        Map<String, Object> params = new HashMap<>();
        params.put("numToRead", numToRead);
        return getNamedParameterJdbcTemplate().query(query, params,
            EXPORT_OUTPUT_ORPHAN_MAPPER);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<InowRelationOrphan> getInowRelationOrphans(int numToRead)
    {

        final String query =
            "select * from ( select ro.sid, ro.inow_profile_sid, ro.related_inow_profile_sid, ro.retry_count, rownum cnt "
                + "from relation_orphans ro where process_time < sysdate "
                + "order by sid ) where rownum <= :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap,
            INOW_RELATION_ORPHAN_MAPPER);
    }

    @Override
    public void deleteDataFileDeleteOrphanId(DataFileOrphanId id)
    {

        String sql = new String("delete from data_file_delete_orphans where sid = ? "
                + " and customer_sid = " + id.getCustomerSid());

        getTemplate().update(sql, new Object[]
        { id.getSid() });

    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#deleteReportingPartnerId(com.infonow.service.recovery.
     * ReportingPartnerOrphanId)
     */
    @Override
    public void deleteReportingPartnerId(ReportingPartnerOrphanId id)
    {
        if (id == null || id.getSid() == null || id.getSid() <= 0)
        {
            return;
        }

        deleteId("reporting_partner_inow_orphs", id.getSid());
    }

    public void deleteInowProfileHierarchyId(InowProfileHierarchyOrphanId id)
    {
        deleteId("inow_profile_hierarchy_orphans", id.getSid());
    }

    public void deleteHierarchyParentId(HierarchyParentOrphanId id)
    {
        deleteId("hierarchy_parent_orphans", id.getSid());
    }

    public void deleteHierarchyParentIds(List<HierarchyParentOrphanId> ids)
    {
        List<Long> sids = new ArrayList<Long>();
        
        for (HierarchyParentOrphanId id : ids) 
        {
            sids.add(id.getSid());            
        }
        
        deleteIds("hierarchy_parent_orphans", sids);
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSalesLineItemNormalizationId(SalesLineItemNormalizationOrphanId id)
    {
        deleteId("sli_normalization_orphans", id.getSid());
    }

    @Override
    public void deleteAddressStandardizationOrphan(Long id)
    {
        deleteId("addr_standardization_orphans", "address_sid", id);
    }

    @Override
    public void deleteInowProfileStandardizationOrphan(InowStandardizationOrphanId id)
    {
        deleteId("inow_profile_std_orphans", "inow_profile_sid", id.getInowProfileSid());
    }

    @Override
    public void deleteInowProfileDedupOrphan(InowProfileOrphanId id)
    {
        deleteId("inow_profile_dedup_orphans", id.getSid());
    }

    @Override
    public void deleteProfilePartnerRematchId(Long id)
    {
        deleteId("profile_rematch_orphans", "profile_partner_sid", id);
    }

    @Override
    public void deleteSliaRematchId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_profile_rematch_orphans", id.getSid());
    }

    @Override
    public void deleteExporterOrphan(ExporterOrphanId id)
    {
        deleteId("exporter_orphans", id.getSid());
    }

    /**
     * Dynamically generates a recovery orphan query for select
     * This function will alternate customers round robin if no orderByClause is specified
     * and there is customer_sid in the columns
     * @param tableName
     * @param columns
     * @param orderByClause
     * @param numIds
     * @param rowMapper
     * @return list of rowMaper
     */
    @SuppressWarnings("rawtypes")
    private List getIds(String tableName, String[] columns, String orderByClause, Integer numIds, RowMapper rowMapper)
    {
        return getIds(tableName, columns, null, orderByClause, numIds, rowMapper);
    }


    /**
     * Dynamically generates a recovery orphan query for select
     * This function will alternate customers round robin if no orderByClause is specified
     * and there is customer_sid in the columns
     * @param tableName
     * @param columns
     * @param additionalWhereClause
     * @param orderByClause
     * @param numIds
     * @param rowMapper
     * @return list of rowMaper
     */
    @SuppressWarnings("rawtypes")
    private List getIds(String tableName, String[] columns, String additionalWhereClause, String orderByClause,
            Integer numIds, RowMapper rowMapper)
    {
        if (columns == null || columns.length <= 0)
        {
            return null;
        }

        StringBuilder sqlBuffer = new StringBuilder("select * from (select ");

        for (int i = 0; i < columns.length; ++i)
        {
            if (i > 0)
            {
                sqlBuffer.append(", ");
            }

            sqlBuffer.append(columns[i]);

            // automatically implement customer 100 functions for all queues.
            if (StringUtils.equalsIgnoreCase(columns[i], "customer_sid"))
            {
                if (i > 0)
                {
                    sqlBuffer.append(", ");
                }

                sqlBuffer.append("rank() over(partition by customer_sid order by sid) rank_by_cust ");

                if (StringUtils.isEmpty(orderByClause))
                {
                    orderByClause = "order by rank_by_cust";
                }

            }

        }

        sqlBuffer.append(" from ");
        sqlBuffer.append(tableName);

        // Construct the WHERE clause
        sqlBuffer.append(" where process_time < sysdate ");
        if (!StringUtils.isEmpty(additionalWhereClause))
        {
            sqlBuffer.append(" and ");
            sqlBuffer.append(additionalWhereClause);
        }

        if (StringUtils.isEmpty(orderByClause))
        {
            sqlBuffer.append(" order by process_time desc ");
        }
        else
        {
            sqlBuffer.append(orderByClause);
        }

        sqlBuffer.append(" ) where rownum <= ?");

        String query = sqlBuffer.toString();

        return getTemplate().query(query, new Object[]
        { numIds }, rowMapper);
    }

    private void deleteId(String tableName, Long sid)
    {
        deleteId(tableName, null, sid);
    }

    private void deleteId(String tableName, String columnName, Long sid)
    {
        StringBuffer sqlBuffer = new StringBuffer("delete from ");

        sqlBuffer.append(tableName);

        if (StringUtils.isEmpty(columnName))
        {
            sqlBuffer.append(" where sid = ?");
        }
        else
        {
            sqlBuffer.append(" where ");
            sqlBuffer.append(columnName);
            sqlBuffer.append(" = ?");
        }

        template.update(sqlBuffer.toString(), new Object[]
        { sid });
    }

    private void deleteIds(String tableName, List<Long> sids)
    {
        for (List<Long> inList : CollectionUtils.partition(sids, 1000))
        {
            deleteIds(tableName, null, inList);
        }
    }

    private void deleteIds(String tableName, String columnName, List<Long> sids)
    {
        StringBuffer sqlBuffer = new StringBuffer("delete from ");

        sqlBuffer.append(tableName);

        if (StringUtils.isEmpty(columnName))
        {
            sqlBuffer.append(" where sid in ");
        }
        else
        {
            sqlBuffer.append(" where ");
            sqlBuffer.append(columnName);
            sqlBuffer.append(" in ");
        }
        
        String deleteQuery = SqlGenerationUtils.buildInClauseVariables(sqlBuffer.toString(), sids.size());
        
        List<Object> params = new ArrayList<Object>();
        params.addAll(sids);

        template.update(deleteQuery, params.toArray());
    }

    /**
     * RowMapper instance used to extract the sid from a query
     */
    private class SidRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return (rs.getLong(1));
        }
    }

    /**
     * RowMapper instance used to extract the line item from a query
     */
    private class LineItemRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            if (rs.getMetaData().getColumnCount() == 2)
            {
                // SID and SLI_SID
                return new LineItemOrphanId(rs.getLong(1), rs.getLong(2), null);
            }
            else
            {
                // SID, SLI_SID and MATCH_TYPE
                return new LineItemOrphanId(rs.getLong(1), rs.getLong(2), rs.getString(3));
            }
        }
    }
    
    /**
     * RowMapper instance used to extract the line item from a query
     */
    private class ValidationRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            ValidationOrphanId orphan = new ValidationOrphanId(rs.getLong(1), rs.getLong(2), rs.getString(3));
            orphan.setCustomerSid(rs.getLong(4));
            orphan.setReportingPartnerSid(rs.getLong(5));
            orphan.setDataFileSid(rs.getLong(6));
            return orphan;
            
        }
    }

    private class ValidationTrackingRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new ValidationTrackingOrphanId(rs.getLong("sid"),
                    rs.getLong("data_file_sid"),
                    rs.getLong("customer_sid"),
                    rs.getDate("create_date"),
                    rs.getDate("update_date"),
                    rs.getLong("retry_count"));

        }
    }

    private class ValidationDeletedRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new ValidationNotificationOrphanId(rs.getLong("sid"),
                    rs.getLong("data_file_sid"),
                    rs.getLong("customer_sid"),
                    rs.getDate("create_date"),
                    rs.getDate("update_date"),
                    rs.getLong("retry_count"));

        }
    }

    private class ReportingPartnerRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new ReportingPartnerOrphanId(rs.getLong(1), rs.getLong(2));
        }
    }

    /**
     * RowMapper instance used to extract the serial number from a query
     */
    private class SerialNumberRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new SerialNumberOrphanId(rs.getLong(1), rs.getLong(2), rs.getString(3));
        }
    }

    private class SliaProfileMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            SalesLineItemAddressOrphanId orphan = new SalesLineItemAddressOrphanId(rs.getLong(1), rs.getLong(2),
                    rs.getLong(3), DataAddressTypeEnum.fromDbCode(rs.getString(4)), rs.getString(5), rs.getLong(6));

            /*
             * the natural key for slia_profile orphans is the address, and any external id for that line item if it is
             * deleted and the customer this is because external id's / deleted line items have different code paths for
             * short circuit matching
             */
            StringBuilder sb = new StringBuilder();
            sb.append(orphan.getAddressSid());
            sb.append(orphan.getCustomerSid());
            // we need this so that we don't cause a race condition on saving the ami data in the amidao
            sb.append(orphan.getSalesLineItemSid());
            switch (orphan.getAddressType())
            {
                case BILL_TO:
                    sb.append(rs.getString("BILL_TO_ADDR_EXTERNAL_ID"));
                    break;
                case SHIP_TO:
                    sb.append(rs.getString("SHIP_TO_ADDR_EXTERNAL_ID"));
                    break;
                case SOLD_TO:
                    sb.append(rs.getString("SOLD_TO_ADDR_EXTERNAL_ID"));
                    break;
                case SHIP_FROM:
                    sb.append(rs.getString("SHIP_FROM_ADDR_EXTERNAL_ID"));
                    break;
                case SELL_FROM:
                    sb.append(rs.getString("SELL_FROM_ADDR_EXTERNAL_ID"));
                    break;
                case SALES_IN:
                    sb.append(rs.getString("SALES_IN_ADDR_EXTERNAL_ID"));
                    break;
                case PURCHASING_CUSTOMER:
                    sb.append(rs.getString("PURCH_CUST_ADDR_EXTERNAL_ID"));
                    break;
                case DERIVED_END_CUSTOMER:
                    sb.append(rs.getString("DER_END_CUST_ADDR_EXTERNAL_ID"));
                    break;
            }

            sb.append(rs.getInt("deleted"));

            orphan.setGroupByValue(sb.toString());

            return orphan;
        }
    }

    private class TupleMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {

            return new SalesLineItemAddressOrphanId(rs.getLong(1), rs.getLong(2), rs.getLong(3),
                    DataAddressTypeEnum.fromDbCode(rs.getString(4)), rs.getString(5), 0l);
        }
    }

    private class InowProfileHierarchyRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            InowProfileHierarchyOrphanId row = new InowProfileHierarchyOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setCustomerSid(rs.getLong("customer_sid"));
            if (rs.wasNull())
            {
                row.setCustomerSid(null);
            }
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));

            row.setHierarchySid(rs.getLong("named_ip_hierarchy_sid"));
            if (rs.wasNull())
            {
                row.setHierarchySid(null);
            }

            Long reprocess = rs.getLong("reprocess_flag");
            if (!rs.wasNull())
            {
                row.setReprocess(reprocess == 0 ? Boolean.FALSE : Boolean.TRUE);
            }

            row.setRetryCount(rs.getLong("retry_count"));
            row.setOrphanKey(rs.getString("orphan_key"));

            return row;
        }
    }
    
    private class HierarchyParentMapper extends InowProfileHierarchyRowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            
            HierarchyParentOrphanId row = new HierarchyParentOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setCustomerSid(rs.getLong("customer_sid"));
            if (rs.wasNull())
            {
                row.setCustomerSid(null);
            }
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));

            row.setHierarchySid(rs.getLong("named_ip_hierarchy_sid"));
            if (rs.wasNull())
            {
                row.setHierarchySid(null);
            }
            
            Long reprocess = rs.getLong("reprocess_flag");
            if (!rs.wasNull())
            {
                row.setReprocess(reprocess == 0 ? Boolean.FALSE : Boolean.TRUE);
            }

            row.setRetryCount(rs.getLong("retry_count"));
            
            row.setGroupByValue(rs.getLong("named_ip_hierarchy_sid"));

            return row;
        }
        
    }
    
    private class AddressHistoryMapper extends GenericMapper implements RowMapper<AddressHistoryOrphanId>
    {
        public AddressHistoryOrphanId mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            this.columnNames = null;
            AddressHistoryOrphanId row = new AddressHistoryOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setAddressHistorySid(rs.getLong("address_history_sid"));
            row.setRetryCount(rs.getLong("retry_count"));
            return row;
        }
    }

    private class InowStandardizationOrphanMapper implements RowMapper<InowStandardizationOrphanId>
    {
        public InowStandardizationOrphanId mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            InowStandardizationOrphanId row = new InowStandardizationOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));
            row.setRetryCount(rs.getLong("retry_count"));
            return row;
        }
    }

    private class InowProfileRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            InowProfileOrphanId row = new InowProfileOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setCustomerSid(rs.getLong("customer_sid"));
            if (rs.wasNull())
            {
                row.setCustomerSid(null);
            }
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));

            return row;
        }
    }

    private class SalesLineItemNormalizationRowMapper extends GenericRowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            Long sid = this.getLong(rs, "sid");
            Long salesLineItemSid = this.getLong(rs, "sales_line_item_sid");
            Long entitySid = this.getLong(rs, "entity_sid");
            Long customerSid = this.getLong(rs, "customer_sid");
            String priceType = this.getString(rs, "price_type");
            SalesLineItemNormalizationOrphanId sliNormOrph = new SalesLineItemNormalizationOrphanId(sid,
                    salesLineItemSid, customerSid, priceType, entitySid);
            return sliNormOrph;
        }
    }

    private class ProfilePartnerRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long customerSid = rs.getLong("customer_sid");
            Long profilePartnerSid = rs.getLong("profile_partner_sid");
            String matchType = rs.getString("match_type");
            ProfilePartnerOrphanId profileSummaryOrphan = new ProfilePartnerOrphanId(sid, customerSid,
                    profilePartnerSid, matchType);
            profileSummaryOrphan.setRetryCount(rs.getLong("retry_count"));
            return profileSummaryOrphan;
        }
    }

    private final class CompositeAccountClassificationOrphanMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int row) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long inowProfileSid = rs.getLong("inow_profile_sid");
            String classificationCode = rs.getString("classification_code");
            return new CompositeAccountClassificationOrphan(sid, inowProfileSid, classificationCode);
        }
    }

    private class ExporterOrphanRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long customerSid = rs.getLong("customer_sid");
            String exportName = rs.getString("export_name");
            ExporterOrphanId id = new ExporterOrphanId();
            id.setSid(sid);
            id.setCustomerSid(customerSid);
            id.setExportName(exportName);
            return id;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<CompositeAccountClassificationOrphan> getCompositeAccountClassificationOrphans(int numToRead)
    {
        return getIds("comp_acct_class_orphan", new String[]
        { "sid", "inow_profile_sid", "classification_code" }, null, numToRead, COMPOSITE_ACCOUNT_CLASS_ORPHAN_MAPPER);
    }

    @Override
    public void deleteCompositeAccountClassificationOrphan(CompositeAccountClassificationOrphan id)
    {
        deleteId("comp_acct_class_orphan", id.getSid());
    }

    private final class SubmissionScheduleOrphanMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int row) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long customerSid = rs.getLong("customer_sid");

            Long submissionPeriodSid = rs.getLong("submission_period_sid");
            if (rs.wasNull())
            {
                submissionPeriodSid = null;
            }

            Long dataFileSid = rs.getLong("data_file_sid");
            if (rs.wasNull())
            {
                dataFileSid = null;
            }

            SubmissionScheduleOrphan.Action action = SubmissionScheduleOrphan.Action.valueOf(rs.getString("action"));
            SubmissionScheduleOrphan orphan = new SubmissionScheduleOrphan(sid, customerSid, submissionPeriodSid,
                    dataFileSid, action);
            orphan.setProcessTime(rs.getTimestamp("process_time"));
            orphan.setCreateDate(rs.getTimestamp("create_date"));
            orphan.setRetryCount(rs.getLong("retry_count"));
            return orphan;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<SubmissionScheduleOrphan> getSubmissionScheduleOrphans(int numToRead)
    {
        // We only want to return one orphan per customer to avoid multi-threaded
        // contention issues with the database. For instance, 2 threads may need to
        // rebuild the same set of data_file_summary_info records for a given data_file.
        // We also want to give the CLEANUP action orphans a lower priority than
        // the normal orphans which is why a decode of the action shows up in the order by
        // within the analytical function row_number.
        String sql = "SELECT * FROM " + "( " + "    SELECT o.sid, " + "           o.customer_sid, "
                + "           o.submission_period_sid, " + "           o.data_file_sid, " + "           o.action, "
                + "           o.process_time, " + "           o.create_date, " + "           o.retry_count, "
                + "           row_number() over (partition by o.customer_sid " + "   order by "
                + "     decode(o.action,'CLEANUP',100,10)," + "     o.process_time) rn "
                + "      FROM submission_schedule_orphan o " + "     WHERE o.process_time < sysdate " + ") "
                + "WHERE rownum < ? " + "  AND rn = 1";

        List<SubmissionScheduleOrphan> orphans = getTemplate().query(sql, new Object[]
        { numToRead }, SUBMISSION_SCHEDULE_ORPHAN_MAPPER);

        return orphans;
    }

    @Override
    public void deleteSubmissionScheduleOrphan(SubmissionScheduleOrphan id)
    {
        deleteId("submission_schedule_orphan", id.getSid());
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<LearnedNameOrphan> getLearnedNameOrphans(int numToRead)
    {
        final String sql = "SELECT * FROM (SELECT o.sid as orphan_sid, l.sid as name_learning_sid, l.from_name, l.to_name, l.approval_status"
                + " FROM name_learning l"
                + " JOIN name_learning_orphan o ON o.name_learning_sid = l.sid"
                + " WHERE o.process_time < sysdate" + " ) WHERE rownum < ?";

        return getTemplate().query(sql, new Object[]
        { numToRead }, LEARNED_NAME_ORPHAN_MAPPER);
    }

    @Override
    public void deleteLearnedNameOrphan(LearnedNameOrphan orphan)
    {
        deleteId("name_learning_orphan", orphan.getSid());
    }

    private static class LearnedNameOrphanMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            LearnedName learnedName = new LearnedName(rs.getString("from_name"), rs.getString("to_name"));
            learnedName.setApprovalStatus(ApprovalStatus.fromString(rs.getString("approval_status")));
            learnedName.setSid(rs.getLong("name_learning_sid"));
            LearnedNameOrphan learnedNameOrphan = new LearnedNameOrphan(learnedName);
            learnedNameOrphan.setSid(rs.getLong("orphan_sid"));
            return learnedNameOrphan;
        }
    }
    
    private static class InowTranslationOrphanMapper implements RowMapper<TranslationOrphan>
    {
        public TranslationOrphan mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            TranslationOrphan translationOrphan = new TranslationOrphan();
            translationOrphan.setSid(rs.getLong("sid"));
            translationOrphan.setInowProfileSid(rs.getLong("inow_profile_sid"));
            translationOrphan.setRetryCount(rs.getLong("retry_count"));
            translationOrphan.setGroupByValue(Math.round(rs.getLong("cnt") / 10));
            return translationOrphan;
        }
    }
    
    private static class SubmissionPeriodUpdateOrphanMapper implements RowMapper<SubmissionPeriodUpdateOrphanId>
    {
        public SubmissionPeriodUpdateOrphanId mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            SubmissionPeriodUpdateOrphanId submissionPeriodUpdateOrphanId = new SubmissionPeriodUpdateOrphanId();
            submissionPeriodUpdateOrphanId.setSid(rs.getLong("sid"));
            submissionPeriodUpdateOrphanId.setCustomerSid(rs.getLong("customer_sid"));
            submissionPeriodUpdateOrphanId.setOldStartDate(rs.getDate("old_start_date"));
            submissionPeriodUpdateOrphanId.setRetryCount(rs.getLong("retry_count"));
            return submissionPeriodUpdateOrphanId;
        }
    }

    private static class InowRelationOrphanMapper
        implements RowMapper<InowRelationOrphan>
    {
        public InowRelationOrphan mapRow(ResultSet rs, int rowNum)
            throws SQLException
        {
            InowRelationOrphan inowRelationOrphan = new InowRelationOrphan();
            inowRelationOrphan.setSid(rs.getLong("sid"));
            inowRelationOrphan.setInowProfileSid(
                rs.getLong("inow_profile_sid"));
            inowRelationOrphan.setRelatedInowProfileSid(
                rs.getLong("related_inow_profile_sid"));
            inowRelationOrphan.setRetryCount(rs.getLong("retry_count"));
            return inowRelationOrphan;
        }
    }

    private static class ExportOutputOrphanMapper
        implements RowMapper<ExportOutputOrphanId>
    {
        public ExportOutputOrphanId mapRow(ResultSet rs, int rowNum)
            throws SQLException
        {
            ExportOutputOrphanId exportOutputOrphan = new ExportOutputOrphanId();
            exportOutputOrphan.setCustomerSid(rs.getLong("CUSTOMER_SID"));
            exportOutputOrphan.setExportOutputSid(
                rs.getLong("EXPORT_OUTPUT_SID"));
            exportOutputOrphan.setSid(rs.getLong("SID"));
            exportOutputOrphan.setRetryCount(rs.getLong("RETRY_COUNT"));
            exportOutputOrphan.setExportRequestSid(
                rs.getLong("EXPORT_REQUEST_SID"));
            exportOutputOrphan.setGroupByValue(rs.getLong("EXPORT_STATUS_SID"));
            exportOutputOrphan.setSessionId(rs.getString("SESSION_ID"));
            return exportOutputOrphan;
        }
    }
}
/**
  * RecoveryQueries.java $Revision: 1 $
  *
  * Copyright (c) 2005-2008 InfoNow Corporation. All rights reserved.
  * INFONOW PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  */

package com.infonow.service.recovery;

import com.infonow.crux.DataFile;
import com.infonow.crux.composite.CompositeAccountClassificationOrphan;
import com.infonow.crux.dataFile.DataFileOrphanId;
import com.infonow.crux.learning.LearnedNameOrphan;
import com.infonow.crux.schedule.submissionSchedule.SubmissionScheduleOrphan;
import com.infonow.service.submissionPeriodUpdate.SubmissionPeriodUpdateOrphanId;
import java.util.List;

/**
 * RecoveryQueries
 *
 * @author clambrecht
 * @version :$
 */
public interface RecoveryQueries
{
    /**
     * Selects input file ids from the orphan table. Limit the number of rows returned to numIds and make sure that the
     * ids returned are greater than the lastId
     *
     * @param numIds
     *            - the number of ids to return (at most)
     * @param lastId
     *            - the last id returned
     * @return List<Long> - a list of ids (may be empty)
     */
    List<Long> getParserOrphans(int numIds, int maxPerCustomer);

    /**
     * Select sales product ids from the orphan table. Limit the number of rows returned to numIds and makes sure that
     * the ids returned are greater than the lastId
     *
     * @param numIds
     *            - the number of ids to return (at most)
     * @param lastId
     *            - the last id returned
     * @return List<LineItemOrphanId> - a list of ids (may be empty)
     */
    List<LineItemOrphanId> getSalesProductOrphanIds(int numIds);

    /**
     * Select sales line item serial number ids from the orphan table. Limit the number of rows returned to numIds and
     * makes sure that the ids returned are greater than the lastId
     *
     * @param numIds
     *            - the number of ids to return (at most)
     * @param lastId
     *            - the last id returned
     * 
     * @return List<SerialNumberOrphanId> - a list of ids (may be empty)
     */
    List<SerialNumberOrphanId> getSalesLineItemSerialNumberOrphanIds(int numIds);

    /**
     * Select inv product ids from the orphan table. Limit the number of rows returned to numIds and makes sure that the
     * ids returned are greater than the lastId
     *
     * @param numIds
     *            - the number of ids to return (at most)
     * @param lastId
     *            - the last id returned
     * @return List<LineItemOrphanId> - a list of ids (may be empty)
     */
    List<LineItemOrphanId> getInvProductOrphanIds(int numIds);

    /**
     * Select Profile Partner ids from the orphan table. Limit the number of rows returned to numIds and makes sure that
     * the ids returned are greater than the lastId
     *
     * @param numIds
     *            - the number of ids to return (at most)
     * @param lastId
     *            - the last id returned
     * @return List<Long> - a list of ids (may be empty)
     */
    List<ProfilePartnerOrphanId> getProfilePartnerOrphanIds(int numIds);

    /**
     * Select Sales Record Address ids from the orphan table. Limit the number of rows returned to numIds and makes sure
     * that the ids returned are greater than the lastId
     *
     * @param numIds
     *            - the number of ids to return (at most)
     * @param lastId
     *            - the last id returned
     * @return List<SalesLineItemAddressOrphanId> - a list of ids (may be empty)
     */
    List<SalesLineItemAddressOrphanId> getSliaProfileOrphanIds(int numIds);

    /**
     * Select Validation Tracking ids from the orphan table. Limit the number of rows returned to numIds and makes sure
     * that the ids returned are greater than the lastId
     *
     * @param numToRead
     *            - the number of ids to return (at most)
     * @return List<ValidationTrackingOrphanId> - a list of ids (may be empty)
     */
    List<ValidationTrackingOrphanId> getValidationTrackingOrphanIds(int numToRead);

    /**
     * Select Validation Deleted ids from the orphan table. Limit the number of rows returned to numIds and makes sure
     * that the ids returned are greater than the lastId
     *
     * @param numToRead
     *            - the number of ids to return (at most)
     * @return List<ValidationTrackingOrphanId> - a list of ids (may be empty)
     */
    List<ValidationNotificationOrphanId> getValidationNotificationOrphanIds(int numToRead);

    /**
     * Select Sales Record Address identifiers from the orphan table associated with composite account processing. Limit
     * the number of rows returned to numIds and makes sure that the identifiers returned are greater than the lastId
     *
     * @param numIds
     *            The maximum number of identifiers to return.
     * @param lastId
     *            The last identifier returned
     * 
     * @return List<SalesLineItemAddressOrphanId> - a list of ids (may be empty)
     */
    List<SalesLineItemAddressOrphanId> getSliaCompositeAccountOrphanIds(int numIds);

    /**
     * Select Sales Record Address ids from the orphan table. Limit the number of rows returned to numIds and makes sure
     * that the ids returned are greater than the lastId
     *
     * @param numIds
     *            - the number of ids to return (at most)
     * @param lastId
     *            - the last id returned
     * @return List<SalesLineItemAddressOrphanId> - a list of ids (may be empty)
     */
    List<SalesLineItemAddressOrphanId> getSliaReportingPartnerOrphanIds(int numIds);

    /**
     * @param numToRead
     * @param lastId
     * @return
     */
    List<SalesLineItemAddressOrphanId> getSliaValidationOrphanIds(int numToRead);

    /**
     * @param numToRead
     * @param lastId
     * @return
     */
    List<ValidationOrphanId> getSalesLineItemValidationOrphanIds(int numToRead);

    /**
     * @param numToRead
     * @param lastId
     * @return
     */
    List<ValidationOrphanId> getInventoryLineItemValidationOrphanIds(int numToRead);

    /**
     * Retrieve the orphans for the reporting partner InfoNow ID'ing process.
     * 
     * @param numToRead
     *            The size of number of orphans to retrieve.
     * 
     * @return A {@link List} of {@link ReportingPartnerOrphanId} objects, if found in the orphan data store, else a
     *         null or empty list.
     */
    List<ReportingPartnerOrphanId> getReportingPartnerOrphanIds(int numToRead);

    List<InowProfileHierarchyOrphanId> getInowProfileHierarchyOrphanIds(int numToRead);

    List<HierarchyParentOrphanId> getHierarchyParentOrphanIds(int numToRead);
    
    List<AddressHistoryOrphanId> getAddressHistoryOrphanIds(int numToRead, String tableName);

    List<SalesLineItemNormalizationOrphanId> getSalesLineItemNormalizationOrphanIds(int numToRead);

    List<Long> getProfilePartnerRematchOrphanIds(int numToRead);

    List<Long> getAddressStandardizationOrphans(int numToRead);

    List<InowProfileOrphanId> getInowProfileDedupOrphans(int numToRead);

    List<SalesLineItemAddressOrphanId> getSliaRematchOrphanIds(int numToRead);

    List<InowStandardizationOrphanId> getInowProfileStandardizationOrphans(
        int numToRead);

    List<ExporterOrphanId> getExporterOrphanIds(int numIds);

    List<CompositeAccountClassificationOrphan> getCompositeAccountClassificationOrphans(
        int numToRead);

    List<SubmissionScheduleOrphan> getSubmissionScheduleOrphans(int numToRead);

    List<DataFileOrphanId> getDataFileDeleteOrphans(int numToRead);

    List<TranslationOrphan> getTranslationOrphans(int numToRead, String status);

    List<ExportOutputOrphanId> getExportOutputOrphans(int numToRead);

    List<InowRelationOrphan> getInowRelationOrphans(int numToRead);

    void deleteDataFileDeleteOrphanId(DataFileOrphanId id);

    List<LearnedNameOrphan> getLearnedNameOrphans(int numToRead);

    /**
     * Deletes the specified orphan id from the table
     *
     * @param id
     *            - the id to delete
     */
    void deleteDataFileId(Long id);

    /**
     * Deletes the specified orphan id from the table
     *
     * @param id
     *            - the id to delete
     */
    void deleteSalesProductId(LineItemOrphanId id);

    /**
     * Deletes the specified orphan id from the table
     *
     * @param id
     *            - the id to delete
     */
    void deleteSalesLineItemSerialNumberId(SerialNumberOrphanId id);

    /**
     * Deletes the specified orphan id from the table
     *
     * @param id
     *            - the id to delete
     */
    void deleteInvProductId(LineItemOrphanId id);

    /**
     * Deletes the specifed orphan id from the table
     *
     * @param id
     *            - the id to delete
     */
    void deleteProfilePartnerId(ProfilePartnerOrphanId id);

    /**
     * Deletes the specifed orphan id from the table
     *
     * @param id
     *            - the id to delete
     */
    void deleteSliaProfileId(SalesLineItemAddressOrphanId id);

    /**
     * Deletes the specifed orphan id from the orphan table associated with composite account processing
     *
     * @param id
     *            The id to delete from the orphan table.
     */
    void deleteSliaCompositeAccountId(SalesLineItemAddressOrphanId id);

    /**
     * Deletes the specifed orphan id from the table
     *
     * @param id
     *            - the id to delete
     */
    void deleteSliaReportingPartnerId(SalesLineItemAddressOrphanId id); 

    void deleteSalesLineItemNormalizationId(SalesLineItemNormalizationOrphanId id);

    public void deleteInowProfileHierarchyId(InowProfileHierarchyOrphanId id);

    public void deleteHierarchyParentId(HierarchyParentOrphanId id);

    public void deleteHierarchyParentIds(List<HierarchyParentOrphanId> ids);

    public void deleteRegexStandardizationOrphans(List<AddressHistoryOrphanId> ids);

    public void deleteRefDataStandardizationOrphans(List<AddressHistoryOrphanId> ids);
    
    public void deleteAddressHistoryOrphans(List<AddressHistoryOrphanId> ids, String orphanTable);

    public void deleteAddressGradingOrphans(List<AddressHistoryOrphanId> ids);

    public void deleteAddressQualityOrphans(List<AddressHistoryOrphanId> ids);

    /**
     * @param id
     */
    void deleteSliaValidationId(SalesLineItemAddressOrphanId id);

    /**
     * @param list
     */
    void deleteSalesLineItemValidationId(List<ValidationOrphanId> list);

    void deleteValidationTrackingIds(List<ValidationTrackingOrphanId> ids);

    void deleteValidationNotificationIds(List<ValidationNotificationOrphanId> ids);

    /**
     * @param list
     */
    void deleteInventoryLineItemValidationId(List<ValidationOrphanId> list);

    void deleteReportingPartnerId(ReportingPartnerOrphanId id);

    void deleteProfilePartnerRematchId(Long id);

    void deleteAddressStandardizationOrphan(Long id);

    void deleteSliaRematchId(SalesLineItemAddressOrphanId id);

    void deleteInowProfileDedupOrphan(InowProfileOrphanId id);

    void deleteInowProfileStandardizationOrphan(InowStandardizationOrphanId id);

    void deleteExporterOrphan(ExporterOrphanId id);

    void deleteCompositeAccountClassificationOrphan(CompositeAccountClassificationOrphan id);

    void deleteSubmissionScheduleOrphan(SubmissionScheduleOrphan id);

    void deleteLearnedNameOrphan(LearnedNameOrphan orphan);

    /**
     * Deletes the orphans associated with the specific salesLineItemSids from the table
     *
     * @param List<Long>
     *            salesLineItemSids
     */
    void deleteSalesLineItemOrphans(List<Long> salesLineItemSids);

    /**
     * Deletes the line item orphans associated with the data file
     *
     * @param dataFile
     */
    void deleteSalesLineItemOrphans(DataFile dataFile);

    /**
     * Deletes the orphans associated with the specific invLineItemSids from the table
     *
     * @param invLineItemSids
     */
    void deleteInvLineItemOrphans(List<Long> invLineItemSids);

    /**
     * Deletes the line item orphans associated with the data file
     *
     * @param dataFile
     */
    void deleteInvLineItemOrphans(DataFile dataFile);

    /**
     * Deletes the orphans associated with the INOW transaltion
     *
     * @param orphanIds
     */
    void deleteTranslateOrphans(List<TranslationOrphan> orphanIds);

    /**
     * Deletes the orphans associated with the export output
     *
     * @param orphanIds
     */
    void deleteExportOutputOrphans(List<ExportOutputOrphanId> orphanIds);

    /**
     * Deletes the orphans associated with the INOW relation
     *
     * @param orphanIds
     */
    void deleteInowRelationOrphan(InowRelationOrphan orphanId);
    void deleteSubmissionPeriodUpdateOrphans(
        SubmissionPeriodUpdateOrphanId submissionPeriodUpdateOrphanId);

    List<SubmissionPeriodUpdateOrphanId> getSubmissionPeriodUpdateOrphans(
        int numToRead);

    void updateStatusTranslationOrphans(String status, List<Long> translationOrphanSids);

    void test();
}
package com.infonow.service.recovery.controller;

import java.util.Iterator;
import java.util.List;

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.infonow.framework.logging.Level;
import com.infonow.framework.logging.Logger;
import com.infonow.framework.service.context.ServiceContext;
import com.infonow.service.recovery.AbstractRunner;
import com.infonow.service.recovery.TranslationOrphan;

import static org.apache.commons.collections4.CollectionUtils.collect;

public class TranslationController extends ServiceCallerBatchController<TranslationOrphan>
{
    private static final String NAME = "Translation";

    public TranslationController()
    {
        super(NAME);
    }

    @Override
    public List<TranslationOrphan> loadIds(int numToRead)
    {
        List<TranslationOrphan> relationOrphans = getRecoveryQueries().getTranslationOrphans(numToRead,"PENDING");//new attribute here named status, gets all the orphans which are in pending state
        //TranslationController will load orphans that are in PENDING(or NULL for backward compatibility) status to be processed by TranslationService, when does the entry in the table happen?
        return relationOrphans;
    }

    public void changeStatus()
    {
        List<TranslationOrphan> relationOrphans = loadIds(10);

    }

    @Override
    protected RecoveryRunner<List<TranslationOrphan>, ?> createRunner(List<TranslationOrphan> ids)
    {
        return new InowRelationRunner(ids);
    }

    @Override
    protected void cleanUp(RecoveryRunner<List<TranslationOrphan>, ?> runner)
    {
        List<TranslationOrphan> ids = runner.getId();//skip the orphans which are in pending or inprogress state
        //loop over ids, create a new list of ids without inprogress and pending status
        System.out.println("ids");
        System.out.println(ids);
        getRecoveryQueries().deleteTranslateOrphans(ids);
    }

    @Override
    public int getNumberOfIdsPerBatch()
    {
        return getNumberOfIdsToRead();
    }

    /**
     * The Runnable instance use to process a batch of staging Addresses
     */
    private class InowRelationRunner extends AbstractRunner<List<TranslationOrphan>, Object>
    {
        ServiceContext _context = null;

        private Logger LOG = Logger.getLogger(TranslationOrphan.class.getName());

        InowRelationRunner(List<TranslationOrphan> ids)
        {
            super(ids, NAME, getService(), getTransactionTemplate());
            _context = new ServiceContext();
            _context.setContextType("default");
        }

        @Override
        public Object loadDataObject(List<TranslationOrphan> ids)
        {
            return loadMatchType(ids);
        }

        @Override
        public Object loadUserObject(List<TranslationOrphan> ids)
        {
            // There is a bug in AbstractRunner where the userObject and userData are swapped.
            // I don't want to change it because it might break every service, so I'll just
            // flip the objects here like the other services seem to do.
            // This is actually the userData in the service.
            return ids;
        }

        @Override
        public String loadMatchType(List<TranslationOrphan> ids)
        {
            return null;
        }

        @Override
        public ServiceContext createServiceContext(Object dataObject)
        {
            return _context;
        }

        @Override
        protected void handleRunException(Exception exception)
        {
            // We are going to make a new orphan with an incremented retry count
            // and put it back in the queue.
            List<TranslationOrphan> oldOrphans = (List<TranslationOrphan>) this.getId();

            for (Iterator<TranslationOrphan> i = oldOrphans.iterator(); i.hasNext();)
            {
                TranslationOrphan oldOrphan = i.next();

                long newRetryCount = oldOrphan.getRetryCount() + 1;

                // First, compute the severity of the exception to be logged based on the retryCount.
                Level loggingLevel = Level.WARNING;

                // We only make it SEVERE if the retryCount is a multiple of 10
                // to avoid hammering engineering support with e-mails.
                if ((newRetryCount % 10) == 0)
                {
                    loggingLevel = Level.SEVERE;
                }

                LOG.log(loggingLevel,
                        getDescription() + ":  Error processing id - " + getId().toString() + ", re-submitting orphan.",
                        exception);

                PlatformTransactionManager txnMgr = null;
                TransactionStatus status = null;

                try
                {
                    txnMgr = _transactionTemplate.getTransactionManager();
                    status = txnMgr.getTransaction(
                            new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW));

                    double delayMinutesAsDouble = 0.0;
                    int delayMinutesAsInteger = 0;

                    // Compute the delay minutes for the new orphan.
                    // Since there is the potential for numeric overflows,
                    // we are wrapping the math in a try/catch block.
                    try
                    {
                        delayMinutesAsDouble = Math.pow(2, newRetryCount);
                        delayMinutesAsInteger = (int) delayMinutesAsDouble;
                    }
                    catch (Exception e)
                    {
                        delayMinutesAsInteger = 7 * 24 * 60; // Push re-try out to 7 days if numeric overflows occur.
                    }

                    getOrchestrationDao().insertTranslationOrphan(delayMinutesAsInteger, oldOrphan.getInowProfileSid(),
                            newRetryCount);

                    try
                    {
                        if (!status.isRollbackOnly())
                        {
                            txnMgr.commit(status);
                        }
                    }
                    catch (Exception e)
                    {
                        String msg = getDescription() + ":  Error processing id - " + getId().toString();
                        LOG.log(Level.SEVERE, msg, e);
                    }
                }
                catch (Exception e)
                {
                    String msg = getDescription() + ":  Error processing id - " + getId().toString();
                    LOG.log(Level.SEVERE, msg, e);
                }
                finally
                {
                    try
                    {
                        if (txnMgr != null && status != null && !status.isCompleted())
                        {
                            txnMgr.rollback(status);
                        }
                    }
                    catch (Exception ee)
                    {
                        String msg = getDescription() + ":  Error processing id - " + getId().toString();
                        LOG.log(Level.SEVERE, msg, ee);
                    }

                }
            }
        }

    } // end inner-class InowRelationRunner

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>
	<import resource="commonService.xml" />

	<!-- Start of Locator service definition -->
	<bean id="serviceLocatorService"
		class="com.infonow.framework.service.remoting.local.ServiceAccessor">
		<property name="serviceInterface"
			value="com.infonow.framework.service.locator.ServiceLocatorService" />
	</bean>

	<!-- Start of TranslationService definition -->
	<bean id="translationService"
		class="com.modeln.cdm.service.translation.TranslationServiceImpl">

		<property name="serviceLocatorService"
			ref="serviceLocatorService" />

		<property name="configurationServiceUrl"
			value="local://localhost/configurationService" />
		<property name="inowProfileRelationService"
			ref="inowProfileRelationService" />
		<property name="awsSnsClient" ref="awsSnsClient" />
		<property name="inowProfileDao" ref="inowProfileDao" />
		<property name="mappingFileName"
			value="translationMapping.xml" />
		<property name="awsSnsTopic"
			value="${translation.aws.sns.topicArn}" />
		<property name="recoveryQueries"
				  ref="recoveryQueries" />
	</bean>

	<bean id="recoveryQueries" class="com.infonow.service.recovery.RecoveryQueriesImpl">
		<property name="template" ref="nucleusJdbcTemplate"/>
		<property name="namedParameterJdbcTemplate" ref="nucleusNamedParameterJdbcTemplate"/>
	</bean>
    <bean id="inowProfileRelationService"
    class="com.infonow.framework.service.locator.impl.ServiceLocatorServiceProxyFactory">        
        <property name="serviceUrl" value="local://localhost/relationService" />        
        <property name="serviceLocatorService" ref="serviceLocatorService" />        
        <property name="className" value="com.modeln.cdm.service.relation.InowProfileRelationServiceImpl" />    
    </bean>
 
	<bean id="awsSnsClient"
		class="com.modeln.cdm.integration.aws.AmazonSNSClientImpl"
		init-method="startup" destroy-method="shutdown">

		<property name="awsCredentials" ref="awsCredentials" />
	</bean>
	<bean id="inowProfileRelationDao"
		class="com.infonow.crux.dao.orm.hibernate.InowProfileRelationDaoImpl">
		<property name="sessionFactory" ref="nucleusSessionFactory" />
	</bean>

	<bean id="inowProfileDao"
		class="com.infonow.crux.dao.orm.hibernate.InowProfileDao">
		<property name="sessionFactory" ref="nucleusSessionFactory" />
	</bean>

</beans>

/**
 * RecoveryQueriesImpl.java $Revision$
 *
 * Copyright (c) 2005-2008 InfoNow Corporation. All rights reserved.
 * INFONOW PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.infonow.service.recovery;

import com.infonow.crux.DataFile;
import com.infonow.crux.composite.CompositeAccountClassificationOrphan;
import com.infonow.crux.dao.jdbc.mapper.GenericMapper;
import com.infonow.crux.dao.jdbc.mapper.GenericRowMapper;
import com.infonow.crux.dataFile.DataFileOrphanId;
import com.infonow.crux.learning.LearnedName;
import com.infonow.crux.learning.LearnedName.ApprovalStatus;
import com.infonow.crux.learning.LearnedNameOrphan;
import com.infonow.crux.schedule.submissionSchedule.SubmissionScheduleOrphan;
import com.infonow.crux.sqlGeneration.SqlGenerationUtils;
import com.infonow.crux.util.CollectionUtils;
import com.infonow.crux.util.Constants.DataAddressTypeEnum;
import com.infonow.framework.logging.Logger;
import com.infonow.framework.util.spring.JdbcTemplateProxy;
import com.infonow.framework.util.spring.NamedParameterJdbcTemplateProxy;
import com.infonow.service.submissionPeriodUpdate.SubmissionPeriodUpdateOrphanId;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.transaction.annotation.Transactional;


/**
 * Query implementations for the recovery service.
 * 
 * @author Chris Lambrecht
 * 
 * @version $Revision$
 */
@SuppressWarnings("rawtypes")
public class RecoveryQueriesImpl implements RecoveryQueries
{
    private static final Logger LOG = Logger.getLogger(RecoveryQueriesImpl.class.getName());
    private static final String DELETE_RL_ORPHAN = "delete from rl_orphans where master_run_id = ?";
    private final RowMapper SID_MAPPER = new SidRowMapper();
    private final RowMapper LINE_ITEM_MAPPER = new LineItemRowMapper();
    private final RowMapper VALIDATION_ITEM_MAPPER = new ValidationRowMapper();
    private final RowMapper SERIAL_NUMBER_MAPPER = new SerialNumberRowMapper();
    private final RowMapper TUPPLE_MAPPER = new TupleMapper();
    private final RowMapper SLIA_MAPPER = new SliaProfileMapper();
    private final RowMapper INOW_PROFILE_HIERARCHY_MAPPER = new InowProfileHierarchyRowMapper();
    private final RowMapper HIERARCHY_PARENT_MAPPER = new HierarchyParentMapper();
    private final RowMapper ADDRESS_HISTORY_MAPPER = new AddressHistoryMapper();
    private final RowMapper INOW_PROFILE_MAPPER = new InowProfileRowMapper();
    private final RowMapper SALES_LINE_ITEM_NORMALIZATION_MAPPER = new SalesLineItemNormalizationRowMapper();
    private final RowMapper REPORTING_PARTNER_MAPPER = new ReportingPartnerRowMapper();
    private final RowMapper PROFILE_PARTNER_MAPPER = new ProfilePartnerRowMapper();
    private final RowMapper EXPORTER_ORPHAN_MAPPER = new ExporterOrphanRowMapper();
    private final RowMapper COMPOSITE_ACCOUNT_CLASS_ORPHAN_MAPPER = new CompositeAccountClassificationOrphanMapper();
    private final RowMapper SUBMISSION_SCHEDULE_ORPHAN_MAPPER = new SubmissionScheduleOrphanMapper();
    private final RowMapper LEARNED_NAME_ORPHAN_MAPPER = new LearnedNameOrphanMapper();
    private final RowMapper INOW_STANDARDIZATION_ORPHAN_MAPPER = new InowStandardizationOrphanMapper();
    private final RowMapper INOW_TRANSLATION_ORPHAN_MAPPER = new InowTranslationOrphanMapper();
    private final RowMapper SUBMISSION_SCHEDULE_UPDATE_ORPHAN_MAPPER = new SubmissionPeriodUpdateOrphanMapper();
    private final RowMapper INOW_RELATION_ORPHAN_MAPPER = new InowRelationOrphanMapper();
    private final RowMapper EXPORT_OUTPUT_ORPHAN_MAPPER = new ExportOutputOrphanMapper();
    private final RowMapper VALIDATION_TRACKING_ORPHAN_MAPPER = new ValidationTrackingRowMapper();
    private final RowMapper VALIDATION_DELETED_ORPHAN_MAPPER = new ValidationDeletedRowMapper();

    @Autowired
    private JdbcTemplate template;
    @Autowired
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    /**
     * Default Constructor
     */
    public RecoveryQueriesImpl()
    {
        super();
    }

    /**
     * Get the value of the template field.
     * 
     * @return Returns the value of template.
     */
    public JdbcTemplate getTemplate()
    {
        return this.template;
    }

    /**
     * Set the value of the template field.
     * 
     * @param template
     *            The template value to set.
     */
    public void setTemplate(JdbcTemplate template)
    {
        this.template = template;
    }

    /**
     * Get the value of the namedParameterJdbcTemplate field.
     * 
     * @return Returns the value of namedParameterJdbcTemplate.
     */
    public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate()
    {
        System.out.println("in getNamedParameterejdbctem");
        System.out.println(this);
        System.out.println(this.namedParameterJdbcTemplate);

        // Get the current stack trace
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();

        // stackTraceElements[0] is getStackTrace
        // stackTraceElements[1] is getNamedParameterJdbcTemplate
        // stackTraceElements[2] is the method that called getNamedParameterJdbcTemplate
        if (stackTraceElements.length > 2) {
            String callerMethodName = stackTraceElements[2].getMethodName();
            System.out.println("Caller Method: " + callerMethodName);
            System.out.println(this);
            System.out.println(this.namedParameterJdbcTemplate);
        }
        return this.namedParameterJdbcTemplate;
    }

    /**
     * Set the value of the namedParameterJdbcTemplate field.
     * 
     * @param namedParameterJdbcTemplate
     *            The namedParameterJdbcTemplate value to set.
     */
    public void setNamedParameterJdbcTemplate(NamedParameterJdbcTemplate namedParameterJdbcTemplate)
    {
        this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
    }

    /**
     * Inject a reference to the {@link DataSource} to use for all queries
     * 
     * @param dataSource
     *            - database connect source
     */
    public void setDataSource(DataSource dataSource)
    {
        template = new JdbcTemplateProxy(dataSource);
        namedParameterJdbcTemplate = new NamedParameterJdbcTemplateProxy(dataSource);
    }

    /**
     * Selects input file ids from the orphan table. Limit the number of rows returned to numIds and make sure that the
     * ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<Long> getParserOrphans(int numIds, int maxPerCustomer)
    {
        final String sql = "select data_file_sid from ( "
                + "select * from ( "
                + "select ifo.*, "
                + "       rank() over( partition by ifo.customer_sid order by sid ) rank_overall "
                + "from ( "
                + "select ifo.*, "
                + " rank() over( partition by ifo.customer_sid, ifo.type order by nvl(ifo.update_date,ifo.create_date) ) rank_of_type "
                + "from ( " + "select ifo.data_file_sid, " + "       if.customer_sid, " + "       ifo.sid, "
                + "       ifo.process_time, if.create_date, if.update_date, "
                + "       CASE WHEN (instr(upper(data_type),'CATALOG') > 0) THEN 'CATALOG' "
                + "            WHEN (instr(upper(data_type),'PROFILE') > 0) THEN 'PROFILE' "
                + "            ELSE data_type  " + "       END as type " + "from input_file_orphans ifo "
                + "join data_file if on ifo.data_file_sid = if.sid " + "where process_time < sysdate " + ") ifo "
                + ") ifo " + "where ifo.type not in ('CATALOG','PROFILE')  " + "      or ifo.rank_of_type = 1 " + ")  "
                + "where rank_overall <= ? " + "order by rank_overall " + ") where rownum < ? ";

        return getTemplate().query(sql, new Object[]
        { maxPerCustomer, numIds }, SID_MAPPER);
    }

    /**
     * Select sales product ids from the orphan table. Limit the number of rows returned to numIds and makes sure that
     * the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<LineItemOrphanId> getSalesProductOrphanIds(int numIds)
    {

        return getIds("sales_line_item_orphans", new String[]
        { "sid", "sales_line_item_sid", "match_type", "customer_sid" }, null, numIds, LINE_ITEM_MAPPER);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#getSalesLineItemSerialNumberOrphanIds(int,
     * com.infonow.service.recovery.SerialNumberOrphanId)
     */
    @SuppressWarnings("unchecked")
    public List<SerialNumberOrphanId> getSalesLineItemSerialNumberOrphanIds(int numIds)
    {
        return getIds("SLI_SERIAL_NUMBER_ORPHANS", new String[]
        { "sid", "sli_serial_number_sid", "match_type", "customer_sid" }, null, numIds, SERIAL_NUMBER_MAPPER);
    }

    /**
     * Select inv product ids from the orphan table. Limit the number of rows returned to numIds and makes sure that the
     * ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<LineItemOrphanId> getInvProductOrphanIds(int numIds)
    {
        return getIds("inv_line_item_orphans", new String[]
        { "sid", "inv_line_item_sid", "match_type", "customer_sid" }, null, numIds, LINE_ITEM_MAPPER);
    }

    /**
     * Select Profile Partner ids from the orphan table. Limit the number of rows returned to numIds and makes sure that
     * the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<Long> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<ProfilePartnerOrphanId> getProfilePartnerOrphanIds(int numIds)
    {
        final String query = "SELECT" + 
                "    sid," + 
                "    profile_partner_sid," + 
                "    customer_sid," + 
                "    match_type," +
                "    retry_count" +
                " FROM" + 
                "    (" + 
                "        SELECT" + 
                "            *" + 
                "        FROM" + 
                "            (" + 
                "                SELECT" + 
                "                    sid," + 
                "                    profile_partner_sid," + 
                "                    customer_sid," + 
                "                    match_type," + 
                "                    retry_count," +
                "                    RANK() OVER(" + 
                "                        PARTITION BY customer_sid" + 
                "                        ORDER BY" + 
                "                            sid" + 
                "                    ) rank" + 
                "                FROM" + 
                "                    (" + 
                "                        SELECT" + 
                "                            po.sid," + 
                "                            po.profile_partner_sid," + 
                "                            pp.customer_sid," + 
                "                            po.match_type," + 
                "                            po.retry_count" + 
                "                        FROM" + 
                "                            profile_orphans po" + 
                "                            JOIN profile_partner pp ON pp.sid = po.profile_partner_sid" + 
                "                        WHERE" + 
                "                            process_time < SYSDATE AND (pp.address_sid is null or pp.address_sid = 0)" + 
                "                            " + 
                "                        UNION " + 
                "                        " + 
                "                         SELECT" + 
                "                            po.sid," + 
                "                            po.profile_partner_sid," + 
                "                            pp.customer_sid," + 
                "                            po.match_type," +
                "                            po.retry_count" +
                "                        FROM" + 
                "                            profile_orphans po" + 
                "                            JOIN profile_partner pp ON pp.sid = po.profile_partner_sid" + 
                "                            JOIN address_history ah ON ah.address_sid = pp.address_sid" + 
                "                        WHERE" + 
                "                            process_time < SYSDATE AND ah.stage = 'API_ENRICHED' " + 
                "                    )" + 
                "            )" + 
                "        ORDER BY" + 
                "            rank" + 
                "    )" + 
                "WHERE" + 
                "    ROWNUM < ?";

        return getTemplate().query(query, new Object[] { numIds }, PROFILE_PARTNER_MAPPER);
    }

    /**
     * Select Sales Record Address ids from the orphan table. Limit the number of rows returned to numIds and makes sure
     * that the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<SalesLineItemAddressOrphanId> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaProfileOrphanIds(int numIds)
    {
        final String query = "select * from (select * from (select sliapo.sid, "
                + "       sliapo.sales_line_item_sid, " + "       sliapo.address_Sid, " + "       sliapo.address_type, "
                + "       sliapo.match_type, sliapo.customer_sid, sli.BILL_TO_ADDR_EXTERNAL_ID"
                + "       ,sli.SOLD_TO_ADDR_EXTERNAL_ID, sli.SHIP_TO_ADDR_EXTERNAL_ID"
                + "       ,sli.SELL_FROM_ADDR_EXTERNAL_ID, sli.SHIP_FROM_ADDR_EXTERNAL_ID"
                + "       ,sli.SALES_IN_ADDR_EXTERNAL_ID, sli.PURCH_CUST_ADDR_EXTERNAL_ID"
                + "       ,sli.DER_END_CUST_ADDR_EXTERNAL_ID,sli.deleted "
                + "       ,case  when exists (select 1 from address_history ah where ah.address_sid = sliapo.address_sid and ah.stage = 'RAW')"
                + "               then 1 else 0 end raw_exists "
                + "       ,case  when exists (select 1 from address_history ah where ah.address_sid = sliapo.address_sid and ah.stage = 'REF_DATA_ENRICHED') "
                + "               then 1 else 0 end ref_data_exists,"
                + "       rank() over(partition by sliapo.customer_sid order by sliapo.sid) rank "
                + " from slia_profile_orphans sliapo join sales_line_item sli on sli.sid = sliapo.sales_line_item_sid and "
                + " sli.customer_sid = sliapo.customer_sid where process_time < sysdate"
                + " order by rank,address_sid) where (raw_exists = 0 or (raw_exists = 1 and ref_data_exists = 1))) where rownum < ? ";

        return getTemplate().query(query, new Object[] { numIds }, SLIA_MAPPER);

    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#getSliaCompositeAccountOrphanIds(int)
     */
    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaCompositeAccountOrphanIds(int numIds)
    {

        return getIds("slia_composite_acct_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "match_type", "customer_sid" }, null, numIds,
        TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<InowProfileHierarchyOrphanId> getInowProfileHierarchyOrphanIds(int numToRead)
    {

        final String query = "SELECT" + 
                "    * " + 
                " FROM" + 
                "    (" + 
                "        SELECT" + 
                "            *" + 
                "        FROM" + 
                "            (" + 
                "                SELECT" + 
                "                    ipho.sid," + 
                "                    ipho.inow_profile_sid," + 
                "                    ipho.customer_sid," + 
                "                    ipho.named_ip_hierarchy_sid," + 
                "                    ipho.reprocess_flag," + 
                "                    ipho.retry_count," + 
                "                    ipho.priority," + 
                "                    RANK() OVER(" + 
                "                        PARTITION BY ipho.customer_sid" + 
                "                        ORDER BY" + 
                "                            sid ASC" + 
                "                    ) rank," +
                "                    upper(csr.entity_name)" + 
                "                    || '-----'" + 
                "                    || csr.country orphan_key" + 
                "                FROM" + 
                "                    inow_profile_hierarchy_orphans   ipho" + 
                "                    LEFT JOIN csr_overlay_v                    csr ON csr.customer_sid = ipho.customer_sid" + 
                "                                              AND ipho.inow_profile_sid = csr.ip_sid" + 
                "                WHERE" + 
                "                    process_time < sysdate" + 
                "            )" + 
                "        ORDER BY" + 
                "            priority," + 
                "            rank" + 
                "    ) " + 
                " WHERE " + 
                "    ROWNUM < :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, INOW_PROFILE_HIERARCHY_MAPPER);

    }

    @SuppressWarnings("unchecked")
    public List<HierarchyParentOrphanId> getHierarchyParentOrphanIds(int numToRead)
    {

        final String query = "select * from ( " + "select * from ( " + "select hpo.sid, "
                + "       hpo.inow_profile_sid, " + "       hpo.customer_sid, "
                + "       hpo.named_ip_hierarchy_sid, " + "       hpo.reprocess_flag, " + "       hpo.retry_count, "
                + "       hpo.priority, "
                + "       rank() over( partition by hpo.customer_sid order by sid asc) rank "
                + "from hierarchy_parent_orphans hpo " + "where process_time < sysdate) "
                + "order by priority,rank " + ") " + "where rownum < :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, HIERARCHY_PARENT_MAPPER);

    }
    
    @SuppressWarnings("unchecked")
    public List<AddressHistoryOrphanId> getAddressHistoryOrphanIds(int numToRead, String tableName)
    {

        final String query = "select * from ( " + "select * from ( " + "select aho.sid, "
                + "       aho.address_history_sid, aho.retry_count "
                + "from " + tableName + " aho " + "where process_time < sysdate) "
                + "order by sid " + ") " + "where rownum <= :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, ADDRESS_HISTORY_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<SalesLineItemNormalizationOrphanId> getSalesLineItemNormalizationOrphanIds(int numToRead)
    {
        return getIds("sli_normalization_orphans", new String[]
        { "sid", "sales_line_item_sid", "customer_sid", "price_type, entity_sid" }, null, numToRead,
                SALES_LINE_ITEM_NORMALIZATION_MAPPER);
    }

    /**
     * Select Sales Record Address ids from the orphan table. Limit the number of rows returned to numIds and makes sure
     * that the ids returned are greater than the lastId
     * 
     * @param numIds
     *            - the number of ids to return (at most)
     * @return List<SalesLineItemAddressOrphanId> - a list of ids (may be empty)
     */
    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaReportingPartnerOrphanIds(int numIds)
    {
        return getIds("reporting_partner_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "null" }, null, numIds, TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationOrphanId> getSalesLineItemValidationOrphanIds(int numToRead)
    {

        final String query = "select * from ( " + "select sv.sid,sv.sales_line_item_sid,sv.match_type,sv.customer_sid, "
                + " sli.reporting_partner_sid,sli.data_file_sid,rank() over(partition by sv.customer_sid order by sv.create_date) rank_by_cust" 
                + " from sli_validation_orphans sv"
                + " join sales_line_item sli on sli.sid = sv.sales_line_item_sid "
                + " and sli.customer_sid = sli.customer_Sid where process_time < sysdate order by rank_by_cust,sv.create_date " 
                + ") where rownum <= :numToRead ";
        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, VALIDATION_ITEM_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<SalesLineItemAddressOrphanId> getSliaValidationOrphanIds(int numToRead)
    {
        return getIds("slia_validation_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "match_type", "customer_sid" }, null, numToRead,
        TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationTrackingOrphanId> getValidationTrackingOrphanIds(int numToRead)
    {
        return getIds("validation_tracking_orphans", new String[]
                        { "sid", "data_file_sid", "customer_sid",
                                "create_date", "update_date", "retry_count" }, null, numToRead,
                VALIDATION_TRACKING_ORPHAN_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationNotificationOrphanId> getValidationNotificationOrphanIds(int numToRead)
    {
        return getIds("validation_notification_orphans", new String[]
                        { "sid", "data_file_sid", "customer_sid",
                                "create_date", "update_date", "retry_count" }, null, numToRead,
                VALIDATION_DELETED_ORPHAN_MAPPER);
    }

    @SuppressWarnings("unchecked")
    public List<ValidationOrphanId> getInventoryLineItemValidationOrphanIds(int numToRead)
    {
        final String query = "select * from ( " + "select sv.sid,sv.inv_line_item_sid,sv.match_type,sv.customer_sid, "
                + " ili.reporting_partner_sid,ili.data_file_sid,rank() over(partition by sv.customer_sid order by sv.create_date) rank_by_cust" 
                + " from ili_validation_orphans sv"
                + " join inv_line_item ili on ili.sid = sv.inv_line_item_sid "
                + " and ili.customer_sid = ili.customer_Sid where process_time < sysdate order by rank_by_cust,sv.create_date " 
                + ") where rownum <= :numToRead ";
        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap, VALIDATION_ITEM_MAPPER);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#getReportingPartnerOrphanIds(int)
     */
    @SuppressWarnings("unchecked")
    @Override
    public List<ReportingPartnerOrphanId> getReportingPartnerOrphanIds(int numToRead)
    {
        return getIds("reporting_partner_inow_orphs", new String[]
        { "sid", "reporting_partner_sid" }, null, numToRead, REPORTING_PARTNER_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Long> getAddressStandardizationOrphans(int numToRead)
    {
        return getIds("addr_standardization_orphans", new String[]
        { "address_sid" }, null, numToRead, SID_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<InowStandardizationOrphanId> getInowProfileStandardizationOrphans(int numToRead)
    {
        return getIds("inow_profile_std_orphans", new String[]
        { "sid", "inow_profile_sid", "retry_count" }, null, numToRead, INOW_STANDARDIZATION_ORPHAN_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<InowProfileOrphanId> getInowProfileDedupOrphans(int numToRead)
    {
        return getIds("inow_profile_dedup_orphans", new String[]
        { "sid", "inow_profile_sid", "customer_sid" }, null, numToRead, INOW_PROFILE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Long> getProfilePartnerRematchOrphanIds(int numToRead)
    {
        return getIds("profile_rematch_orphans", new String[]
        { "profile_partner_sid" }, null, numToRead, SID_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<SalesLineItemAddressOrphanId> getSliaRematchOrphanIds(int numToRead)
    {
        return getIds("slia_profile_rematch_orphans", new String[]
        { "sid", "sales_line_item_sid", "address_sid", "address_type", "match_type" }, null, numToRead, TUPPLE_MAPPER);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<ExporterOrphanId> getExporterOrphanIds(int numToRead)
    {
        return getIds("exporter_orphans", new String[]
        { "sid", "customer_sid", "export_name" }, null, numToRead, EXPORTER_ORPHAN_MAPPER);
    }

    /**
     * Deletes the specified orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteDataFileId(Long id)
    {
        if (id == null)
        {
            return;
        }

        deleteId("input_file_orphans", "data_file_sid", id);
    }

    /**
     * Deletes the specified orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSalesProductId(LineItemOrphanId id)
    {
        deleteId("sales_line_item_orphans", id.getSid());
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#deleteSalesLineItemSerialNumberId(com.infonow.service.recovery.
     * SerialNumberOrphanId)
     */
    public void deleteSalesLineItemSerialNumberId(SerialNumberOrphanId id)
    {
        deleteId("sli_serial_number_orphans", id.getSid());
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteProfilePartnerId(ProfilePartnerOrphanId id)
    {
        deleteId("profile_orphans", "sid", id.getSid());
    }

    /**
     * Deletes the specified orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteInvProductId(LineItemOrphanId id)
    {
        deleteId("inv_line_item_orphans", id.getSid());
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSliaProfileId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_profile_orphans", id.getSid());
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#deleteSliaCompositeAccountId(com.infonow.service.recovery.
     * SalesLineItemAddressOrphanId)
     */
    public void deleteSliaCompositeAccountId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_composite_acct_orphans", id.getSid());
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSliaReportingPartnerId(SalesLineItemAddressOrphanId id)
    {
        deleteId("reporting_partner_orphans", id.getSid());
    }

    public void deleteSalesLineItemValidationId(List<ValidationOrphanId> ids)
    {
        String sql = new String("delete from sli_validation_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    public void deleteValidationTrackingIds(List<ValidationTrackingOrphanId> ids)
    {
        String sql = new String("delete from validation_tracking_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    public void deleteValidationNotificationIds(List<ValidationNotificationOrphanId> ids)
    {
        String sql = new String("delete from validation_notification_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    public void deleteSliaValidationId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_validation_orphans", id.getSid());
    }

    public void deleteInventoryLineItemValidationId(List<ValidationOrphanId> ids)
    {
        String sql = new String("delete from ili_validation_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        ids.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteSalesLineItemOrphans(List<Long> salesLineItemSids)
    {
        String sql = new String("delete from sales_line_item_orphans where sales_line_item_sid in (:sids)");

        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", salesLineItemSids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteSalesLineItemOrphans(DataFile dataFile)
    {
        String sql = new String("delete from sales_line_item_orphans where sales_line_item_sid in "
                + " (select sid from sales_line_item where data_file_sid = ? and customer_sid = "
                + dataFile.getCustomer().getSid().toString() + ")");

        getTemplate().update(sql, new Object[]
        { dataFile.getSid() });
    }

    @Override
    public void deleteInvLineItemOrphans(List<Long> invLineItemSids)
    {
        String sql = new String("delete from inv_line_item_orphans where inv_line_item_sid in (:sids)");
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", invLineItemSids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Transactional
    @Override
    public void updateStatusTranslationOrphans(String status, List<Long> translationOrphanSids)//pass the array of sids, and pass status
    {
        String sql = new String("update translation_orphans set status = :status where sid in (:sids)");
        String commit = new String("commit");
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("sids", translationOrphanSids);
        paramMap.put("status", status);
        System.out.println("getNamedParameterJdbcTemplate() in update status method: "+getNamedParameterJdbcTemplate());
        System.out.println(getNamedParameterJdbcTemplate());
        if(getNamedParameterJdbcTemplate() != null || getTemplate() != null)//why are these null?
        {
            int rowsUpdated = getNamedParameterJdbcTemplate().update(sql, paramMap);
            System.out.println("Number of rows updated: " + rowsUpdated);
            getNamedParameterJdbcTemplate().query(commit,SID_MAPPER);
        }
        else{
            System.out.println("printing getNamedParameterJdbcTemplate : "+getNamedParameterJdbcTemplate());//why is this null???
            System.out.println("printing getTemplate : "+getTemplate());
            System.out.println("getNamedParameterJdbcTemplate is null");
        }
    }

    @Override
    public void test()//pass the array of sids, and pass status
    {

        String sql = new String("select sid from translation_orphans where sid = :sid");

        System.out.println("getNamedParameterJdbcTemplate() in test method: "+getNamedParameterJdbcTemplate());
        System.out.println(getNamedParameterJdbcTemplate());
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("sid", 2);
        System.out.println("getNamedParameterJdbcTemplate from test method : "+getNamedParameterJdbcTemplate());
        if(getNamedParameterJdbcTemplate() != null || getTemplate() != null)//why are these null?
        {
            getTemplate().update(sql,paramMap);
            getNamedParameterJdbcTemplate().update(sql, paramMap);
        }
        else{
            System.out.println("printing getNamedParameterJdbcTemplate : "+getNamedParameterJdbcTemplate());//why is this null???
            System.out.println("printing getTemplate : "+getTemplate());
            System.out.println("getNamedParameterJdbcTemplate is null");
        }
    }
    
    public void deleteRegexStandardizationOrphans(List<AddressHistoryOrphanId> orphanIds)//follow this
    {
        String sql = new String("delete from regex_std_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }
    
    @Override
    public void deleteRefDataStandardizationOrphans(List<AddressHistoryOrphanId> orphanIds)
    {
        String sql = new String("delete from ref_data_std_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }
    
    @Override
    public void deleteAddressHistoryOrphans(List<AddressHistoryOrphanId> orphanIds, String orphanTable)
    {
        String sql = new String("delete from " + orphanTable + " where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }
    
    @Override
    public void deleteAddressGradingOrphans(List<AddressHistoryOrphanId> orphanIds)
    {
        String sql = new String("delete from address_grading_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteTranslateOrphans(List<TranslationOrphan> orphanIds)
    {
        for (List<TranslationOrphan> orphans : CollectionUtils.partition(orphanIds, 1000))
        {
            String sql = new String(
                "delete from translation_orphans where sid in (:sids)");
            List<Long> sids = new ArrayList<>();
            orphans.forEach(ad -> sids.add(ad.getSid()));
            Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
            paramMap.put("sids", sids);

            getNamedParameterJdbcTemplate().update(sql, paramMap);
        }
    }

    @Override
    public void deleteExportOutputOrphans(List<ExportOutputOrphanId> orphanIds)
    {

        String sql = new String(
            "delete from export_output_orphans where sid in (:sids)");
        Map<String, Object> paramMap = new HashMap<String, Object>();
        List<Long> sids = orphanIds.stream().map(o -> o.getSid())
            .collect(Collectors.toList());
        List<List<Long>> sidsPartition = CollectionUtils.partition(sids, 1000);
        for (List<Long> batch : sidsPartition)
        {
            paramMap.put("sids", sids);

            getNamedParameterJdbcTemplate().update(sql, paramMap);
        }
    }

    @Override
    public void deleteSubmissionPeriodUpdateOrphans(
        SubmissionPeriodUpdateOrphanId orphanId)
    {

        String sql = new String(
            "delete from SP_UPDATE_ORPHANS where sid in (:sid)");
        Map<String, Long> paramMap = new HashMap<String, Long>();
        paramMap.put("sid", orphanId.getSid());

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteInowRelationOrphan(InowRelationOrphan orphanId)
    {
        String sql = new String("delete from relation_orphans where sid = :sid");
        Map<String, Long> paramMap = new HashMap<String, Long>();
        paramMap.put("sid", orphanId.getSid());

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteAddressQualityOrphans(List<AddressHistoryOrphanId> orphanIds)
    {
        String sql = new String("delete from address_quality_orphans where sid in (:sids)");
        List<Long> sids = new ArrayList<>();
        orphanIds.forEach(ad -> sids.add(ad.getSid()));
        Map<String, List<Long>> paramMap = new HashMap<String, List<Long>>();
        paramMap.put("sids", sids);

        getNamedParameterJdbcTemplate().update(sql, paramMap);
    }

    @Override
    public void deleteInvLineItemOrphans(DataFile dataFile)
    {
        String sql = new String("delete from inv_line_item_orphans where inv_line_item_sid in "
                + " (select sid from inv_line_item where data_file_sid = ? and customer_sid = "
                + dataFile.getCustomer().getSid().toString() + ")");

        getTemplate().update(sql, new Object[]
        { dataFile.getSid() });
    }

    @Override
    public List<DataFileOrphanId> getDataFileDeleteOrphans(int numToRead)
    {
        List<?> orphanIds = null;

        orphanIds = getIds("data_file_delete_orphans", new String[]
        { "sid", "data_file_sid", "customer_sid", "reprocess", "user_login", "retry_count", "preserve_date" }, null, null, numToRead, new GenericRowMapper()
        {
            @Override
            public Object mapRow(ResultSet rs, int arg1) throws SQLException
            {

                DataFileOrphanId dfoi = new DataFileOrphanId();
                dfoi.setDataFileSid(this.getLong(rs, "data_file_sid"));
                dfoi.setCustomerSid(this.getLong(rs, "customer_sid"));
                dfoi.setReprocess(this.getBooleanValue(rs, "reprocess"));
                dfoi.setUserLogin(this.getString(rs, "user_login"));
                dfoi.setSid(this.getLong(rs, "sid"));
                dfoi.setRetryCount(this.getLong(rs, "retry_count"));
                dfoi.setPreserveDate(this.getBooleanValue(rs, "preserve_date"));
                return dfoi;
            }

        });

        return (List<DataFileOrphanId>) orphanIds;
    }

    @SuppressWarnings("unchecked")
    public List<TranslationOrphan> getTranslationOrphans(int numToRead, String status)
    {

        String query = "select * from ( select ipro.sid, ipro.inow_profile_sid, ipro.retry_count, ipro.status, rownum cnt "
                + "from translation_orphans ipro where process_time < sysdate "
                + "order by sid ) result where rownum <= :numToRead ";


        Map<String, String> paramMap = new HashMap<String, String>();
        paramMap.put("numToRead", String.valueOf(numToRead));
        if(status!=null){
            query = query + "and result.status = :status";
            paramMap.put("status",status);
        }

        System.out.println("getNamedParameterJdbcTemplate() in method getTranslationOrphans : "+getNamedParameterJdbcTemplate());
        System.out.println(getNamedParameterJdbcTemplate());

        return getNamedParameterJdbcTemplate().query(query, paramMap, INOW_TRANSLATION_ORPHAN_MAPPER);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<SubmissionPeriodUpdateOrphanId> getSubmissionPeriodUpdateOrphans(int numToRead)
    {

        final String query =
            "select * from ( select spuo.sid, spuo.customer_sid, spuo.old_start_date, spuo.retry_count, rownum cnt "
                + "from sp_update_orphans spuo where process_time < sysdate "
                + "order by sid ) where rownum <= :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap,
            SUBMISSION_SCHEDULE_UPDATE_ORPHAN_MAPPER);
    }

    @Override
    public List<ExportOutputOrphanId> getExportOutputOrphans(int numToRead)
    {
        final String query = "WITH rowlimit AS ("
            + "SELECT * FROM ("
            + "SELECT EXPORT_STATUS_SID FROM export_output eo JOIN export_output_orphans eoo "
            + "ON eoo.EXPORT_OUTPUT_SID= eo.sid AND eo.customer_sid = eoo.customer_sid "
            + "WHERE eoo.process_time < sysdate + interval '10' second "
            + "GROUP BY EXPORT_STATUS_SID ) "
            + "WHERE rownum < :numToRead ) "
            + "SELECT eoo.SID,eoo.CUSTOMER_SID,eoo.SESSION_ID,eoo.EXPORT_OUTPUT_SID,"
            + "eo.EXPORT_STATUS_SID,eoo.retry_count,es.export_name, eoo.export_request_sid"
            + ",RANK() OVER (PARTITION BY eo.customer_sid,eo.EXPORT_STATUS_SID ORDER BY eoo.sid ) rankof "
            + "FROM EXPORT_OUTPUT eo JOIN EXPORT_OUTPUT_ORPHANS eoo ON eoo.EXPORT_OUTPUT_SID = eo.sid "
            + "JOIN rowlimit ON rowlimit.export_status_sid = eo.EXPORT_STATUS_SID "
            + "join export_status es on es.sid = eo.export_status_sid and es.customer_sid = eo.customer_sid "
            + "ORDER BY rankof";

        Map<String, Object> params = new HashMap<>();
        params.put("numToRead", numToRead);
        return getNamedParameterJdbcTemplate().query(query, params,
            EXPORT_OUTPUT_ORPHAN_MAPPER);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<InowRelationOrphan> getInowRelationOrphans(int numToRead)
    {

        final String query =
            "select * from ( select ro.sid, ro.inow_profile_sid, ro.related_inow_profile_sid, ro.retry_count, rownum cnt "
                + "from relation_orphans ro where process_time < sysdate "
                + "order by sid ) where rownum <= :numToRead ";

        Map<String, Integer> paramMap = new HashMap<String, Integer>();
        paramMap.put("numToRead", numToRead);

        return getNamedParameterJdbcTemplate().query(query, paramMap,
            INOW_RELATION_ORPHAN_MAPPER);
    }

    @Override
    public void deleteDataFileDeleteOrphanId(DataFileOrphanId id)
    {

        String sql = new String("delete from data_file_delete_orphans where sid = ? "
                + " and customer_sid = " + id.getCustomerSid());

        getTemplate().update(sql, new Object[]
        { id.getSid() });

    }

    /*
     * (non-Javadoc)
     * 
     * @see com.infonow.service.recovery.RecoveryQueries#deleteReportingPartnerId(com.infonow.service.recovery.
     * ReportingPartnerOrphanId)
     */
    @Override
    public void deleteReportingPartnerId(ReportingPartnerOrphanId id)
    {
        if (id == null || id.getSid() == null || id.getSid() <= 0)
        {
            return;
        }

        deleteId("reporting_partner_inow_orphs", id.getSid());
    }

    public void deleteInowProfileHierarchyId(InowProfileHierarchyOrphanId id)
    {
        deleteId("inow_profile_hierarchy_orphans", id.getSid());
    }

    public void deleteHierarchyParentId(HierarchyParentOrphanId id)
    {
        deleteId("hierarchy_parent_orphans", id.getSid());
    }

    public void deleteHierarchyParentIds(List<HierarchyParentOrphanId> ids)
    {
        List<Long> sids = new ArrayList<Long>();
        
        for (HierarchyParentOrphanId id : ids) 
        {
            sids.add(id.getSid());            
        }
        
        deleteIds("hierarchy_parent_orphans", sids);
    }

    /**
     * Deletes the specifed orphan id from the table
     * 
     * @param id
     *            - the id to delete
     */
    public void deleteSalesLineItemNormalizationId(SalesLineItemNormalizationOrphanId id)
    {
        deleteId("sli_normalization_orphans", id.getSid());
    }

    @Override
    public void deleteAddressStandardizationOrphan(Long id)
    {
        deleteId("addr_standardization_orphans", "address_sid", id);
    }

    @Override
    public void deleteInowProfileStandardizationOrphan(InowStandardizationOrphanId id)
    {
        deleteId("inow_profile_std_orphans", "inow_profile_sid", id.getInowProfileSid());
    }

    @Override
    public void deleteInowProfileDedupOrphan(InowProfileOrphanId id)
    {
        deleteId("inow_profile_dedup_orphans", id.getSid());
    }

    @Override
    public void deleteProfilePartnerRematchId(Long id)
    {
        deleteId("profile_rematch_orphans", "profile_partner_sid", id);
    }

    @Override
    public void deleteSliaRematchId(SalesLineItemAddressOrphanId id)
    {
        deleteId("slia_profile_rematch_orphans", id.getSid());
    }

    @Override
    public void deleteExporterOrphan(ExporterOrphanId id)
    {
        deleteId("exporter_orphans", id.getSid());
    }

    /**
     * Dynamically generates a recovery orphan query for select
     * This function will alternate customers round robin if no orderByClause is specified
     * and there is customer_sid in the columns
     * @param tableName
     * @param columns
     * @param orderByClause
     * @param numIds
     * @param rowMapper
     * @return list of rowMaper
     */
    @SuppressWarnings("rawtypes")
    private List getIds(String tableName, String[] columns, String orderByClause, Integer numIds, RowMapper rowMapper)
    {
        return getIds(tableName, columns, null, orderByClause, numIds, rowMapper);
    }


    /**
     * Dynamically generates a recovery orphan query for select
     * This function will alternate customers round robin if no orderByClause is specified
     * and there is customer_sid in the columns
     * @param tableName
     * @param columns
     * @param additionalWhereClause
     * @param orderByClause
     * @param numIds
     * @param rowMapper
     * @return list of rowMaper
     */
    @SuppressWarnings("rawtypes")
    private List getIds(String tableName, String[] columns, String additionalWhereClause, String orderByClause,
            Integer numIds, RowMapper rowMapper)
    {
        if (columns == null || columns.length <= 0)
        {
            return null;
        }

        StringBuilder sqlBuffer = new StringBuilder("select * from (select ");

        for (int i = 0; i < columns.length; ++i)
        {
            if (i > 0)
            {
                sqlBuffer.append(", ");
            }

            sqlBuffer.append(columns[i]);

            // automatically implement customer 100 functions for all queues.
            if (StringUtils.equalsIgnoreCase(columns[i], "customer_sid"))
            {
                if (i > 0)
                {
                    sqlBuffer.append(", ");
                }

                sqlBuffer.append("rank() over(partition by customer_sid order by sid) rank_by_cust ");

                if (StringUtils.isEmpty(orderByClause))
                {
                    orderByClause = "order by rank_by_cust";
                }

            }

        }

        sqlBuffer.append(" from ");
        sqlBuffer.append(tableName);

        // Construct the WHERE clause
        sqlBuffer.append(" where process_time < sysdate ");
        if (!StringUtils.isEmpty(additionalWhereClause))
        {
            sqlBuffer.append(" and ");
            sqlBuffer.append(additionalWhereClause);
        }

        if (StringUtils.isEmpty(orderByClause))
        {
            sqlBuffer.append(" order by process_time desc ");
        }
        else
        {
            sqlBuffer.append(orderByClause);
        }

        sqlBuffer.append(" ) where rownum <= ?");

        String query = sqlBuffer.toString();

        return getTemplate().query(query, new Object[]
        { numIds }, rowMapper);
    }

    private void deleteId(String tableName, Long sid)
    {
        deleteId(tableName, null, sid);
    }

    private void deleteId(String tableName, String columnName, Long sid)
    {
        StringBuffer sqlBuffer = new StringBuffer("delete from ");

        sqlBuffer.append(tableName);

        if (StringUtils.isEmpty(columnName))
        {
            sqlBuffer.append(" where sid = ?");
        }
        else
        {
            sqlBuffer.append(" where ");
            sqlBuffer.append(columnName);
            sqlBuffer.append(" = ?");
        }

        template.update(sqlBuffer.toString(), new Object[]
        { sid });
    }

    private void deleteIds(String tableName, List<Long> sids)
    {
        for (List<Long> inList : CollectionUtils.partition(sids, 1000))
        {
            deleteIds(tableName, null, inList);
        }
    }

    private void deleteIds(String tableName, String columnName, List<Long> sids)
    {
        StringBuffer sqlBuffer = new StringBuffer("delete from ");

        sqlBuffer.append(tableName);

        if (StringUtils.isEmpty(columnName))
        {
            sqlBuffer.append(" where sid in ");
        }
        else
        {
            sqlBuffer.append(" where ");
            sqlBuffer.append(columnName);
            sqlBuffer.append(" in ");
        }
        
        String deleteQuery = SqlGenerationUtils.buildInClauseVariables(sqlBuffer.toString(), sids.size());
        
        List<Object> params = new ArrayList<Object>();
        params.addAll(sids);

        template.update(deleteQuery, params.toArray());
    }

    /**
     * RowMapper instance used to extract the sid from a query
     */
    private class SidRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return (rs.getLong(1));
        }
    }

    /**
     * RowMapper instance used to extract the line item from a query
     */
    private class LineItemRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            if (rs.getMetaData().getColumnCount() == 2)
            {
                // SID and SLI_SID
                return new LineItemOrphanId(rs.getLong(1), rs.getLong(2), null);
            }
            else
            {
                // SID, SLI_SID and MATCH_TYPE
                return new LineItemOrphanId(rs.getLong(1), rs.getLong(2), rs.getString(3));
            }
        }
    }
    
    /**
     * RowMapper instance used to extract the line item from a query
     */
    private class ValidationRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            ValidationOrphanId orphan = new ValidationOrphanId(rs.getLong(1), rs.getLong(2), rs.getString(3));
            orphan.setCustomerSid(rs.getLong(4));
            orphan.setReportingPartnerSid(rs.getLong(5));
            orphan.setDataFileSid(rs.getLong(6));
            return orphan;
            
        }
    }

    private class ValidationTrackingRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new ValidationTrackingOrphanId(rs.getLong("sid"),
                    rs.getLong("data_file_sid"),
                    rs.getLong("customer_sid"),
                    rs.getDate("create_date"),
                    rs.getDate("update_date"),
                    rs.getLong("retry_count"));

        }
    }

    private class ValidationDeletedRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new ValidationNotificationOrphanId(rs.getLong("sid"),
                    rs.getLong("data_file_sid"),
                    rs.getLong("customer_sid"),
                    rs.getDate("create_date"),
                    rs.getDate("update_date"),
                    rs.getLong("retry_count"));

        }
    }

    private class ReportingPartnerRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new ReportingPartnerOrphanId(rs.getLong(1), rs.getLong(2));
        }
    }

    /**
     * RowMapper instance used to extract the serial number from a query
     */
    private class SerialNumberRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            return new SerialNumberOrphanId(rs.getLong(1), rs.getLong(2), rs.getString(3));
        }
    }

    private class SliaProfileMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            SalesLineItemAddressOrphanId orphan = new SalesLineItemAddressOrphanId(rs.getLong(1), rs.getLong(2),
                    rs.getLong(3), DataAddressTypeEnum.fromDbCode(rs.getString(4)), rs.getString(5), rs.getLong(6));

            /*
             * the natural key for slia_profile orphans is the address, and any external id for that line item if it is
             * deleted and the customer this is because external id's / deleted line items have different code paths for
             * short circuit matching
             */
            StringBuilder sb = new StringBuilder();
            sb.append(orphan.getAddressSid());
            sb.append(orphan.getCustomerSid());
            // we need this so that we don't cause a race condition on saving the ami data in the amidao
            sb.append(orphan.getSalesLineItemSid());
            switch (orphan.getAddressType())
            {
                case BILL_TO:
                    sb.append(rs.getString("BILL_TO_ADDR_EXTERNAL_ID"));
                    break;
                case SHIP_TO:
                    sb.append(rs.getString("SHIP_TO_ADDR_EXTERNAL_ID"));
                    break;
                case SOLD_TO:
                    sb.append(rs.getString("SOLD_TO_ADDR_EXTERNAL_ID"));
                    break;
                case SHIP_FROM:
                    sb.append(rs.getString("SHIP_FROM_ADDR_EXTERNAL_ID"));
                    break;
                case SELL_FROM:
                    sb.append(rs.getString("SELL_FROM_ADDR_EXTERNAL_ID"));
                    break;
                case SALES_IN:
                    sb.append(rs.getString("SALES_IN_ADDR_EXTERNAL_ID"));
                    break;
                case PURCHASING_CUSTOMER:
                    sb.append(rs.getString("PURCH_CUST_ADDR_EXTERNAL_ID"));
                    break;
                case DERIVED_END_CUSTOMER:
                    sb.append(rs.getString("DER_END_CUST_ADDR_EXTERNAL_ID"));
                    break;
            }

            sb.append(rs.getInt("deleted"));

            orphan.setGroupByValue(sb.toString());

            return orphan;
        }
    }

    private class TupleMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {

            return new SalesLineItemAddressOrphanId(rs.getLong(1), rs.getLong(2), rs.getLong(3),
                    DataAddressTypeEnum.fromDbCode(rs.getString(4)), rs.getString(5), 0l);
        }
    }

    private class InowProfileHierarchyRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            InowProfileHierarchyOrphanId row = new InowProfileHierarchyOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setCustomerSid(rs.getLong("customer_sid"));
            if (rs.wasNull())
            {
                row.setCustomerSid(null);
            }
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));

            row.setHierarchySid(rs.getLong("named_ip_hierarchy_sid"));
            if (rs.wasNull())
            {
                row.setHierarchySid(null);
            }

            Long reprocess = rs.getLong("reprocess_flag");
            if (!rs.wasNull())
            {
                row.setReprocess(reprocess == 0 ? Boolean.FALSE : Boolean.TRUE);
            }

            row.setRetryCount(rs.getLong("retry_count"));
            row.setOrphanKey(rs.getString("orphan_key"));

            return row;
        }
    }
    
    private class HierarchyParentMapper extends InowProfileHierarchyRowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            
            HierarchyParentOrphanId row = new HierarchyParentOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setCustomerSid(rs.getLong("customer_sid"));
            if (rs.wasNull())
            {
                row.setCustomerSid(null);
            }
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));

            row.setHierarchySid(rs.getLong("named_ip_hierarchy_sid"));
            if (rs.wasNull())
            {
                row.setHierarchySid(null);
            }
            
            Long reprocess = rs.getLong("reprocess_flag");
            if (!rs.wasNull())
            {
                row.setReprocess(reprocess == 0 ? Boolean.FALSE : Boolean.TRUE);
            }

            row.setRetryCount(rs.getLong("retry_count"));
            
            row.setGroupByValue(rs.getLong("named_ip_hierarchy_sid"));

            return row;
        }
        
    }
    
    private class AddressHistoryMapper extends GenericMapper implements RowMapper<AddressHistoryOrphanId>
    {
        public AddressHistoryOrphanId mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            this.columnNames = null;
            AddressHistoryOrphanId row = new AddressHistoryOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setAddressHistorySid(rs.getLong("address_history_sid"));
            row.setRetryCount(rs.getLong("retry_count"));
            return row;
        }
    }

    private class InowStandardizationOrphanMapper implements RowMapper<InowStandardizationOrphanId>
    {
        public InowStandardizationOrphanId mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            InowStandardizationOrphanId row = new InowStandardizationOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));
            row.setRetryCount(rs.getLong("retry_count"));
            return row;
        }
    }

    private class InowProfileRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            InowProfileOrphanId row = new InowProfileOrphanId();
            row.setSid(rs.getLong("sid"));
            row.setCustomerSid(rs.getLong("customer_sid"));
            if (rs.wasNull())
            {
                row.setCustomerSid(null);
            }
            row.setInowProfileSid(rs.getLong("inow_profile_sid"));

            return row;
        }
    }

    private class SalesLineItemNormalizationRowMapper extends GenericRowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            Long sid = this.getLong(rs, "sid");
            Long salesLineItemSid = this.getLong(rs, "sales_line_item_sid");
            Long entitySid = this.getLong(rs, "entity_sid");
            Long customerSid = this.getLong(rs, "customer_sid");
            String priceType = this.getString(rs, "price_type");
            SalesLineItemNormalizationOrphanId sliNormOrph = new SalesLineItemNormalizationOrphanId(sid,
                    salesLineItemSid, customerSid, priceType, entitySid);
            return sliNormOrph;
        }
    }

    private class ProfilePartnerRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long customerSid = rs.getLong("customer_sid");
            Long profilePartnerSid = rs.getLong("profile_partner_sid");
            String matchType = rs.getString("match_type");
            ProfilePartnerOrphanId profileSummaryOrphan = new ProfilePartnerOrphanId(sid, customerSid,
                    profilePartnerSid, matchType);
            profileSummaryOrphan.setRetryCount(rs.getLong("retry_count"));
            return profileSummaryOrphan;
        }
    }

    private final class CompositeAccountClassificationOrphanMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int row) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long inowProfileSid = rs.getLong("inow_profile_sid");
            String classificationCode = rs.getString("classification_code");
            return new CompositeAccountClassificationOrphan(sid, inowProfileSid, classificationCode);
        }
    }

    private class ExporterOrphanRowMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long customerSid = rs.getLong("customer_sid");
            String exportName = rs.getString("export_name");
            ExporterOrphanId id = new ExporterOrphanId();
            id.setSid(sid);
            id.setCustomerSid(customerSid);
            id.setExportName(exportName);
            return id;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<CompositeAccountClassificationOrphan> getCompositeAccountClassificationOrphans(int numToRead)
    {
        return getIds("comp_acct_class_orphan", new String[]
        { "sid", "inow_profile_sid", "classification_code" }, null, numToRead, COMPOSITE_ACCOUNT_CLASS_ORPHAN_MAPPER);
    }

    @Override
    public void deleteCompositeAccountClassificationOrphan(CompositeAccountClassificationOrphan id)
    {
        deleteId("comp_acct_class_orphan", id.getSid());
    }

    private final class SubmissionScheduleOrphanMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int row) throws SQLException
        {
            Long sid = rs.getLong("sid");
            Long customerSid = rs.getLong("customer_sid");

            Long submissionPeriodSid = rs.getLong("submission_period_sid");
            if (rs.wasNull())
            {
                submissionPeriodSid = null;
            }

            Long dataFileSid = rs.getLong("data_file_sid");
            if (rs.wasNull())
            {
                dataFileSid = null;
            }

            SubmissionScheduleOrphan.Action action = SubmissionScheduleOrphan.Action.valueOf(rs.getString("action"));
            SubmissionScheduleOrphan orphan = new SubmissionScheduleOrphan(sid, customerSid, submissionPeriodSid,
                    dataFileSid, action);
            orphan.setProcessTime(rs.getTimestamp("process_time"));
            orphan.setCreateDate(rs.getTimestamp("create_date"));
            orphan.setRetryCount(rs.getLong("retry_count"));
            return orphan;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<SubmissionScheduleOrphan> getSubmissionScheduleOrphans(int numToRead)
    {
        // We only want to return one orphan per customer to avoid multi-threaded
        // contention issues with the database. For instance, 2 threads may need to
        // rebuild the same set of data_file_summary_info records for a given data_file.
        // We also want to give the CLEANUP action orphans a lower priority than
        // the normal orphans which is why a decode of the action shows up in the order by
        // within the analytical function row_number.
        String sql = "SELECT * FROM " + "( " + "    SELECT o.sid, " + "           o.customer_sid, "
                + "           o.submission_period_sid, " + "           o.data_file_sid, " + "           o.action, "
                + "           o.process_time, " + "           o.create_date, " + "           o.retry_count, "
                + "           row_number() over (partition by o.customer_sid " + "   order by "
                + "     decode(o.action,'CLEANUP',100,10)," + "     o.process_time) rn "
                + "      FROM submission_schedule_orphan o " + "     WHERE o.process_time < sysdate " + ") "
                + "WHERE rownum < ? " + "  AND rn = 1";

        List<SubmissionScheduleOrphan> orphans = getTemplate().query(sql, new Object[]
        { numToRead }, SUBMISSION_SCHEDULE_ORPHAN_MAPPER);

        return orphans;
    }

    @Override
    public void deleteSubmissionScheduleOrphan(SubmissionScheduleOrphan id)
    {
        deleteId("submission_schedule_orphan", id.getSid());
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<LearnedNameOrphan> getLearnedNameOrphans(int numToRead)
    {
        final String sql = "SELECT * FROM (SELECT o.sid as orphan_sid, l.sid as name_learning_sid, l.from_name, l.to_name, l.approval_status"
                + " FROM name_learning l"
                + " JOIN name_learning_orphan o ON o.name_learning_sid = l.sid"
                + " WHERE o.process_time < sysdate" + " ) WHERE rownum < ?";

        return getTemplate().query(sql, new Object[]
        { numToRead }, LEARNED_NAME_ORPHAN_MAPPER);
    }

    @Override
    public void deleteLearnedNameOrphan(LearnedNameOrphan orphan)
    {
        deleteId("name_learning_orphan", orphan.getSid());
    }

    private static class LearnedNameOrphanMapper implements RowMapper
    {
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            LearnedName learnedName = new LearnedName(rs.getString("from_name"), rs.getString("to_name"));
            learnedName.setApprovalStatus(ApprovalStatus.fromString(rs.getString("approval_status")));
            learnedName.setSid(rs.getLong("name_learning_sid"));
            LearnedNameOrphan learnedNameOrphan = new LearnedNameOrphan(learnedName);
            learnedNameOrphan.setSid(rs.getLong("orphan_sid"));
            return learnedNameOrphan;
        }
    }
    
    private static class InowTranslationOrphanMapper implements RowMapper<TranslationOrphan>
    {
        public TranslationOrphan mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            TranslationOrphan translationOrphan = new TranslationOrphan();
            translationOrphan.setSid(rs.getLong("sid"));
            translationOrphan.setInowProfileSid(rs.getLong("inow_profile_sid"));
            translationOrphan.setRetryCount(rs.getLong("retry_count"));
            translationOrphan.setGroupByValue(Math.round(rs.getLong("cnt") / 10));
            return translationOrphan;
        }
    }
    
    private static class SubmissionPeriodUpdateOrphanMapper implements RowMapper<SubmissionPeriodUpdateOrphanId>
    {
        public SubmissionPeriodUpdateOrphanId mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            SubmissionPeriodUpdateOrphanId submissionPeriodUpdateOrphanId = new SubmissionPeriodUpdateOrphanId();
            submissionPeriodUpdateOrphanId.setSid(rs.getLong("sid"));
            submissionPeriodUpdateOrphanId.setCustomerSid(rs.getLong("customer_sid"));
            submissionPeriodUpdateOrphanId.setOldStartDate(rs.getDate("old_start_date"));
            submissionPeriodUpdateOrphanId.setRetryCount(rs.getLong("retry_count"));
            return submissionPeriodUpdateOrphanId;
        }
    }

    private static class InowRelationOrphanMapper
        implements RowMapper<InowRelationOrphan>
    {
        public InowRelationOrphan mapRow(ResultSet rs, int rowNum)
            throws SQLException
        {
            InowRelationOrphan inowRelationOrphan = new InowRelationOrphan();
            inowRelationOrphan.setSid(rs.getLong("sid"));
            inowRelationOrphan.setInowProfileSid(
                rs.getLong("inow_profile_sid"));
            inowRelationOrphan.setRelatedInowProfileSid(
                rs.getLong("related_inow_profile_sid"));
            inowRelationOrphan.setRetryCount(rs.getLong("retry_count"));
            return inowRelationOrphan;
        }
    }

    private static class ExportOutputOrphanMapper
        implements RowMapper<ExportOutputOrphanId>
    {
        public ExportOutputOrphanId mapRow(ResultSet rs, int rowNum)
            throws SQLException
        {
            ExportOutputOrphanId exportOutputOrphan = new ExportOutputOrphanId();
            exportOutputOrphan.setCustomerSid(rs.getLong("CUSTOMER_SID"));
            exportOutputOrphan.setExportOutputSid(
                rs.getLong("EXPORT_OUTPUT_SID"));
            exportOutputOrphan.setSid(rs.getLong("SID"));
            exportOutputOrphan.setRetryCount(rs.getLong("RETRY_COUNT"));
            exportOutputOrphan.setExportRequestSid(
                rs.getLong("EXPORT_REQUEST_SID"));
            exportOutputOrphan.setGroupByValue(rs.getLong("EXPORT_STATUS_SID"));
            exportOutputOrphan.setSessionId(rs.getString("SESSION_ID"));
            return exportOutputOrphan;
        }
    }
}
package com.modeln.cdm.service.translation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.infonow.framework.util.spring.JdbcTemplateProxy;
import com.infonow.framework.util.spring.NamedParameterJdbcTemplateProxy;
import com.infonow.service.recovery.RecoveryQueries;
import com.infonow.service.recovery.RecoveryQueriesImpl;
import com.infonow.service.recovery.controller.TranslationController;
import org.json.JSONException;
import org.json.JSONObject;

import com.infonow.crux.dao.InowProfileDao;
import com.infonow.crux.impl.InowProfileImpl;
import com.infonow.framework.service.ServiceResponse;
import com.infonow.framework.service.configuration.support.ServiceDefinition;
import com.infonow.framework.service.context.ServiceContext;
import com.infonow.framework.service.support.ServiceException;
import com.infonow.service.configuration.ConfigurationException;
import com.infonow.service.configuration.impl.AbstractBaseServiceWithConfiguration;
import com.infonow.service.recovery.TranslationOrphan;
import com.modeln.cdm.integration.aws.AmazonSNSClientImpl;
import com.modeln.cdm.service.translation.configuration.TranslationConfguration;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import software.amazon.awssdk.utils.StringUtils;

import javax.sql.DataSource;

@ServiceDefinition(name = "translationService", abstraction = com.modeln.cdm.service.translation.TranslationService.class, implementation = com.modeln.cdm.service.translation.TranslationServiceImpl.class)
public class TranslationServiceImpl extends AbstractBaseServiceWithConfiguration implements TranslationService
{
    private InowProfileRelationService inowProfileRelationService;

    private RecoveryQueries _recoveryQueries;

    private AmazonSNSClientImpl awsSnsClient;

    private String awsSnsTopic;

    private InowProfileDao inowProfileDao;

    private static String UNWANTED_CHARS;

    private static String LATIN_CHARS;

    private static Pattern UNWANTED_PATTERN;

    private static Pattern LATIN_PATTERN;

    private TranslationConfguration translationConfig;

    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    private JdbcTemplate template;


    private TranslationController translationController;

    Matcher matcher;

    public TranslationServiceImpl()
    {
        if (UNWANTED_CHARS == null)
        {
            UNWANTED_CHARS = "[\\u0020-\\u002F\\u003A-\\u0040\\u005B-\\u0060\\u007B-\\u007E\\u0030-\\u0039\\u00A0-\\u00BF\\u2013-\\u204A]";
        }

        if (LATIN_CHARS == null)
        {
            LATIN_CHARS = "[\\u0041-\\u005A\\u0061-\\u007A\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u00FF\\uFF00-\\uFF60]+";
        }

        if (UNWANTED_PATTERN == null)
        {
            UNWANTED_PATTERN = Pattern.compile(UNWANTED_CHARS);
        }

        if (LATIN_PATTERN == null)
        {
            LATIN_PATTERN = Pattern.compile(LATIN_CHARS);
        }
    }

    public RecoveryQueries getRecoveryQueries()
    {
        if (_recoveryQueries == null)
        {
            throw new IllegalStateException("The attribute _recoveryQueries can not be null");
        }
        return _recoveryQueries;
    }
//    public RecoveryQueries getRecoveryQueries()
//    {
//        return recoveryQueries;
//    }

    public void setRecoveryQueries(RecoveryQueries recoveryQueries)
    {
        _recoveryQueries = recoveryQueries;
    }
    public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate()
    {
        return this.namedParameterJdbcTemplate;
    }

    @SuppressWarnings("unchecked")
    @Override
    public ServiceResponse execute(ServiceContext serviceContext, Object userObject, Object userData)
            throws ServiceException
    {
        if (!(userData instanceof List<?>))
        {
            throw new IllegalArgumentException("Argument userData must be a List but was " + userData == null ? null
                    : userData.getClass().getName());
        }

        System.out.println("userdata : ");
        System.out.println(userData);
        List<TranslationOrphan> ids = ((List<TranslationOrphan>) userData);
        List<JSONObject> addrs = new ArrayList<JSONObject>();
        translationConfig = getTranslationConfig();

        String sql = "update translation_orphans set status = :status where sid = :sid";
        List<Long> translationIds = new ArrayList<Long>();
        for (TranslationOrphan id : ids)
        {
            Long sid = id.getSid();
//            Map<String, Object> paramMap = new HashMap<String, Object>();
//            paramMap.put("sid", sid);
//            paramMap.put("status","IN PROGRESS");
            InowProfileImpl inowProfile = (InowProfileImpl) getInowProfileDao().getBySid(id.getInowProfileSid());

            boolean entityNeedsTranslation = isNonLatin(inowProfile.getName());

            String address = inowProfile.getStreet1() + inowProfile.getStreet2() + inowProfile.getCity()
                    + inowProfile.getStateProvince() + inowProfile.getPostalCode();
            boolean addressNeedsTranslation = isNonLatin(address);//The method returns true if the percentage of Latin characters in the cleaned addressComponent is below the threshold specified in translationConfig.

            //entity needs translation and address needs translation both are false => addr turns out to be null
            JSONObject addr = createTranslationInput(inowProfile, entityNeedsTranslation, addressNeedsTranslation);
//            if(getNamedParameterJdbcTemplate() != null)
//            {
//                getNamedParameterJdbcTemplate().update(sql, paramMap);
//            }

            translationIds.add(sid);//just trying it out
            if (addr != null)
            {
                translationIds.add(sid);
                addrs.add(addr);
                //ids.remove()//give the id of that particualr orphan
            }

        }
//        getRecoveryQueries().test();//is it because of getRecoveryQueries
        changeStatus("IN PROGRESS",translationIds);//just trying it out
        if (!addrs.isEmpty())//addrs list is empty, when does it enter into this condition??
        {
            publish(addrs);
            changeStatus("IN PROGRESS",translationIds);
            //call a method, pass translation orphans ids list
        }

        return new ServiceResponse("Success", ServiceResponse.SUCCESS);
    }

    public void changeStatus(String status,List<Long> translationIds){
        getRecoveryQueries().updateStatusTranslationOrphans(status, translationIds);
    }

    protected JSONObject createTranslationInput(InowProfileImpl inowProfile, boolean entityNeedsTranslation,
            boolean addressNeedsTranslation) throws ServiceException
    {
        JSONObject addr = null;

        if ((entityNeedsTranslation || addressNeedsTranslation)
                && isTranslationNeededForCountry(inowProfile.getCountryObject().getTwoCharCode()) && !isInowRelationAlreadyExists(inowProfile))
        {
            addr = new JSONObject();
            //pick the orphans which are in pending state
            /*Fetch the pending orphans.
            Update their status to IN PROGRESS.
            Save the updated status back to the database.
             */

//            loadIds(10);


//            //change the value of the attribute status in the table translation_orphans from pending to in progress
//            TranslationController translationController = new TranslationController();
//            List<TranslationOrphan> pendingOrphans = translationController.getPendingOrphans();//these orphans must be sent to the amazon SQS and the status of those orphans must be changed to in progress
//            translationController.updateOrphanStatus(pendingOrphans, "IN PROGRESS");//is this where im supposed to change the
            //status of the orphan, I dont think so since im updating the status of all the orphans which are in pending
            //state but I'll only have to update the status of the orphan which is picked up AWS queue(SQS)

            System.out.println("inow id : "+inowProfile.getId());

            try
            {
                addr.put("id", inowProfile.getIdentifier());

                JSONObject addrDetails = new JSONObject();

                if (entityNeedsTranslation)
                {
                    if (!StringUtils.isEmpty(inowProfile.getName()))
                    {
                        addrDetails.put("name", inowProfile.getName());
                    }
                }
                if (addressNeedsTranslation)
                {
                    if (!StringUtils.isEmpty(inowProfile.getStreet1()))
                    {
                        addrDetails.put("street1", inowProfile.getStreet1());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getStreet2()))
                    {
                        addrDetails.put("street2", inowProfile.getStreet2());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getCity()))
                    {
                        addrDetails.put("city", inowProfile.getCity());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getStateProvince()))
                    {
                        addrDetails.put("stateprovince", inowProfile.getStateProvince());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getPostalCode()))
                    {
                        addrDetails.put("postalcode", inowProfile.getPostalCode());
                    }
                    if (!StringUtils.isEmpty(inowProfile.getCountry()))
                    {
                        addrDetails.put("country", inowProfile.getCountryObject().getTwoCharCode());
                    }
                }

                addr.put("query", addrDetails);
                addr.put("target", "EN");
            }
            catch (JSONException e)
            {
                throw new ServiceException("Exception while converting InowProfile to JSONObject" + e);
            }
        }

        return addr;

    }

    public void publish(List<JSONObject> addrs) throws ServiceException
    {
        //here change the orphan's status to in progress

        try
        {
            getAwsSnsClient().publishSingleMessages(getAwsSnsTopic(), addrs);
        }
        catch (Exception e)
        {
            throw new ServiceException("Exception while publishing message to Amazon SNS", e);
        }
    }

    private boolean isTranslationNeededForCountry(String country)
    {
        return translationConfig.getCountries().contains(country);
    }

    private boolean isNonLatin(String addressComponent)
    {
        addressComponent = addressComponent.replace("null", "");

        matcher = UNWANTED_PATTERN.matcher(addressComponent);
        String fulladdress = matcher.replaceAll("");

        if (StringUtils.isEmpty(fulladdress))
        {
            return false;
        }

        String latinAddress = "";

        matcher = LATIN_PATTERN.matcher(fulladdress);
        while (matcher.find())
        {
            latinAddress += matcher.group();
        }

        float matchPercent = (latinAddress.length() * 100 / fulladdress.length());

        return matchPercent < translationConfig.getThreshold();
    }

    private boolean isInowRelationAlreadyExists(InowProfileImpl inowProfile)
    {
        return getInowProfileRelationService().isRelationExists(inowProfile.getSid());
    }

    private TranslationConfguration getTranslationConfig()
    {
        ServiceContext configContext = new ServiceContext(null, null);

        try
        {
            return (TranslationConfguration) getRequiredConfiguration(configContext);
        }
        catch (ConfigurationException e)
        {
            throw new IllegalStateException(e);
        }
    }

    public AmazonSNSClientImpl getAwsSnsClient()
    {
        return awsSnsClient;
    }

    public void setAwsSnsClient(AmazonSNSClientImpl awsSnsClient)
    {
        this.awsSnsClient = awsSnsClient;
    }

    public String getAwsSnsTopic()
    {
        return awsSnsTopic;
    }

    public void setAwsSnsTopic(String awsSnsTopic)
    {
        this.awsSnsTopic = awsSnsTopic;
    }

    public InowProfileDao getInowProfileDao()
    {
        return inowProfileDao;
    }

    public void setInowProfileDao(InowProfileDao inowProfileDao)
    {
        this.inowProfileDao = inowProfileDao;
    }

    public InowProfileRelationService getInowProfileRelationService()
    {
        return inowProfileRelationService;
    }

    public void setInowProfileRelationService(InowProfileRelationService inowProfileRelationService)
    {
        this.inowProfileRelationService = inowProfileRelationService;
    }

}
-- Determining what factors were more prevalent across all collisions
SELECT 
    COUNT(*) AS TOTAL_COLLISIONS,
    (SUM(CASE WHEN INATTENTIONIND = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*)) AS PERC_INATTENTIONIND,
    (SUM(CASE WHEN UNDERINFL = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*)) AS PERC_UNDERINFL,
    (SUM(CASE WHEN SPEEDING = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*)) AS PERC_SPEEDING,
    (SUM(CASE WHEN HITPARKEDCAR = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*)) AS PERC_HITPARKEDCAR,
    (SUM(CASE WHEN POORDRIVINGCOND = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*)) AS PERC_POORDRIVINGCOND,
    (SUM(CASE WHEN NIGH