All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openfact.services.resources.admin.DocumentsAdminResource Maven / Gradle / Ivy

The newest version!
package org.openfact.services.resources.admin;

import oasis.names.specification.ubl.schema.xsd.commonbasiccomponents_21.IDType;
import oasis.names.specification.ubl.schema.xsd.creditnote_21.CreditNoteType;
import oasis.names.specification.ubl.schema.xsd.debitnote_21.DebitNoteType;
import oasis.names.specification.ubl.schema.xsd.invoice_21.InvoiceType;
import org.apache.commons.io.IOUtils;
import org.jboss.logging.Logger;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.json.JSONObject;
import org.openfact.Config;
import org.openfact.common.ClientConnection;
import org.openfact.common.converts.DocumentUtils;
import org.openfact.events.admin.OperationType;
import org.openfact.events.admin.ResourceType;
import org.openfact.models.*;
import org.openfact.models.search.SearchCriteriaFilterModel;
import org.openfact.models.search.SearchCriteriaFilterOperator;
import org.openfact.models.search.SearchCriteriaModel;
import org.openfact.models.search.SearchResultsModel;
import org.openfact.models.types.DestinyType;
import org.openfact.models.types.DocumentRequiredAction;
import org.openfact.models.types.DocumentType;
import org.openfact.models.types.SendEventStatus;
import org.openfact.models.utils.ModelToRepresentation;
import org.openfact.models.utils.RepresentationToModel;
import org.openfact.report.ExportFormat;
import org.openfact.report.ReportException;
import org.openfact.report.ReportTemplateConfiguration;
import org.openfact.report.ReportTemplateProvider;
import org.openfact.representations.idm.DocumentRepresentation;
import org.openfact.representations.idm.SendEventRepresentation;
import org.openfact.representations.idm.ThirdPartyEmailRepresentation;
import org.openfact.representations.idm.search.SearchCriteriaRepresentation;
import org.openfact.representations.idm.search.SearchResultsRepresentation;
import org.openfact.services.ErrorResponse;
import org.openfact.services.ModelErrorResponseException;
import org.openfact.services.managers.DocumentManager;
import org.openfact.services.managers.EventStoreManager;
import org.openfact.services.managers.OrganizationManager;
import org.openfact.services.resource.security.OrganizationAuth;
import org.openfact.services.resource.security.Resource;
import org.openfact.services.resource.security.SecurityContextProvider;
import org.openfact.ubl.UBLIDGenerator;
import org.openfact.ubl.UBLReaderWriter;
import org.openfact.ubl.utils.DefaultUBLUtil;
import org.openfact.ubl.utils.UBLUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Stateless
@Consumes(MediaType.APPLICATION_JSON)
public class DocumentsAdminResource {

    private static final String UPLOAD_FILE_NAME = "file";
    private static final Logger logger = Logger.getLogger(DocumentsAdminResource.class);

    @Context
    protected UriInfo uriInfo;

    @Context
    protected OpenfactSession session;

    @Context
    protected ClientConnection clientConnection;

    @Inject
    private OrganizationManager organizationManager;

    @Inject
    private DocumentManager documentManager;

    @Inject
    private EventStoreManager eventStoreManager;

    @Inject
    private SecurityContextProvider securityContext;

    @Inject
    private DocumentProvider documentProvider;

    @Inject
    private ModelToRepresentation modelToRepresentation;

    @Inject
    private RepresentationToModel representationToModel;

    @Inject
    private ReportTemplateProvider reportTemplate;

    @Inject
    private UBLUtil ublUtil;

    private OrganizationModel getOrganizationModel(String organizationName) {
        OrganizationModel organization = organizationManager.getOrganizationByName(organizationName);
        if (organization == null) {
            throw new NotFoundException("Organization " + organizationName + " not found.");
        }
        return organization;
    }

    private DocumentModel getDocument(String documentIdPk, OrganizationModel organization) {
        DocumentModel document = documentProvider.getDocumentById(documentIdPk, organization);
        if (document == null) {
            throw new NotFoundException("Document not found.");
        }
        return document;
    }

    private OrganizationAuth getAuth(OrganizationModel organization) {
        List permittedOrganizations = securityContext.getPermittedOrganizations(session);
        if (!securityContext.getClientUser(session).hasAppRole(AdminRoles.ADMIN) && !permittedOrganizations.contains(organization)) {
            throw new ForbiddenException();
        }
        return securityContext.getClientUser(session).organizationAuth(Resource.DOCUMENT);
    }

    private AdminEventBuilder getAdminEvent(OrganizationModel organization) {
        return new AdminEventBuilder(organization, securityContext.getClientUser(session), session, clientConnection).resource(ResourceType.DOCUMENT);
    }

    private List getInputs(MultipartFormDataInput multipartFormDataInput) {
        Map> uploadForm = multipartFormDataInput.getFormDataMap();
        return uploadForm.get(UPLOAD_FILE_NAME);
    }

    private  T readFile(InputPart inputPart, DocumentType documentType, Class tClass) throws IOException, ModelException {
        InputStream inputStream = inputPart.getBody(InputStream.class, null);
        byte[] bytes = IOUtils.toByteArray(inputStream);

        Config.Scope documentConfig = Config.scope(documentType.toString().toLowerCase());
        String readerWriterProviderType = documentConfig.get(DefaultUBLUtil.READER_WRITER, "default");

        UBLReaderWriter readerWriter = (UBLReaderWriter) ublUtil.getReaderWriter(readerWriterProviderType, documentType.toString());
        if (readerWriter == null) {
            throw new ModelException("Could not find a valid " + UBLReaderWriter.class.getSimpleName() + " for type[" + documentType.toString() + "]");
        }

        T t = readerWriter.reader().read(bytes);
        if (t == null) {
            throw new ModelException("Could not read file");
        }

        return t;
    }

    @POST
    @Path("/upload")
    @Consumes("multipart/form-data")
    @Produces(MediaType.APPLICATION_JSON)
    public void uploadDocument(@PathParam("organization") final String organizationName, final MultipartFormDataInput input) throws ModelErrorResponseException {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireManage();

        List inputParts = getInputs(input);
        for (InputPart inputPart : inputParts) {
            try {
                InputStream inputStream = inputPart.getBody(InputStream.class, null);
                Document xml = DocumentUtils.getInputStreamToDocument(inputStream);

                Element documentElement = xml.getDocumentElement();
                String documentType = documentElement.getTagName();
                switch (documentType) {
                    case "Invoice":
                        InvoiceType invoiceType = readFile(inputPart, DocumentType.INVOICE, InvoiceType.class);
                        uploadInvoice(organization, invoiceType);
                        break;
                    case "CreditNote":
                        CreditNoteType creditNoteType = readFile(inputPart, DocumentType.CREDIT_NOTE, CreditNoteType.class);
                        uploadCreditNote(organization, creditNoteType);
                        break;
                    case "DebitNote":
                        DebitNoteType debitNoteType = readFile(inputPart, DocumentType.DEBIT_NOTE, DebitNoteType.class);
                        uploadDebitNote(organization, debitNoteType);
                        break;
                    default:
                        throw new ModelErrorResponseException("Error Reading data, Document type not recognized", Response.Status.BAD_REQUEST);
                }
            } catch (IOException e) {
                logger.error("Error reading input data", e);
                throw new ModelErrorResponseException("Error Reading data", Response.Status.BAD_REQUEST);
            } catch (ModelDuplicateException e) {
                throw new ModelErrorResponseException("Invoice exists with same documentId");
            } catch (ModelException e) {
                throw new ModelErrorResponseException("Could not create document");
            } catch (Exception e) {
                throw new ModelErrorResponseException("Error Reading data", Response.Status.BAD_REQUEST);
            }
        }
    }

    private DocumentModel uploadInvoice(OrganizationModel organization, InvoiceType invoiceType) throws ModelException {
        // Double-check duplicated documentId
        if (invoiceType.getIDValue() != null && documentManager.getDocumentByTypeAndDocumentId(DocumentType.INVOICE, invoiceType.getIDValue(), organization) != null) {
            throw new ModelDuplicateException("Invoice exists with same documentId[" + invoiceType.getIDValue() + "]");
        }

        IDType documentId = invoiceType.getID();
        if (documentId == null) {
            UBLIDGenerator ublIDGenerator = ublUtil.getIDGenerator(DocumentType.INVOICE);
            if (ublIDGenerator == null) {
                throw new ModelException("Could not find a valid " + UBLIDGenerator.class.getSimpleName() + " for type[" + DocumentType.INVOICE.toString() + "]");
            }

            String newDocumentId = ublIDGenerator.generateID(organization, invoiceType);
            documentId = new IDType(newDocumentId);
            invoiceType.setID(documentId);
        }

        DocumentModel document = documentManager.addDocument(organization, invoiceType.getIDValue(), DocumentType.INVOICE, invoiceType);
        eventStoreManager.send(organization, getAdminEvent(organization)
                .operation(OperationType.CREATE)
                .resourcePath(uriInfo, document.getId())
                //.representation(invoiceType)
                .getEvent());

        return document;
    }

    private DocumentModel uploadCreditNote(OrganizationModel organization, CreditNoteType creditNoteType) throws ModelException {
        // Double-check duplicated documentId
        if (creditNoteType.getIDValue() != null && documentManager.getDocumentByTypeAndDocumentId(DocumentType.CREDIT_NOTE, creditNoteType.getIDValue(), organization) != null) {
            throw new ModelDuplicateException("Credit Note exists with same documentId[" + creditNoteType.getIDValue() + "]");
        }

        IDType documentId = creditNoteType.getID();
        if (documentId == null) {
            UBLIDGenerator ublIDGenerator = ublUtil.getIDGenerator(DocumentType.CREDIT_NOTE);
            if (ublIDGenerator == null) {
                throw new ModelException("Could not find a valid " + UBLIDGenerator.class.getSimpleName() + " for type[" + DocumentType.CREDIT_NOTE.toString() + "]");
            }

            String newDocumentId = ublIDGenerator.generateID(organization, creditNoteType);
            documentId = new IDType(newDocumentId);
            creditNoteType.setID(documentId);
        }

        DocumentModel document = documentManager.addDocument(organization, creditNoteType.getIDValue(), DocumentType.CREDIT_NOTE, creditNoteType);
        eventStoreManager.send(organization, getAdminEvent(organization)
                .operation(OperationType.CREATE)
                .resourcePath(uriInfo, document.getId())
                //.representation(creditNoteType)
                .getEvent());

        return document;
    }

    private DocumentModel uploadDebitNote(OrganizationModel organization, DebitNoteType debitNoteType) throws ModelException {
        // Double-check duplicated documentId
        if (debitNoteType.getIDValue() != null && documentManager.getDocumentByTypeAndDocumentId(DocumentType.DEBIT_NOTE, debitNoteType.getIDValue(), organization) != null) {
            throw new ModelDuplicateException("Debit Note exists with same documentId[" + debitNoteType.getIDValue() + "]");
        }

        IDType documentId = debitNoteType.getID();
        if (documentId == null) {
            UBLIDGenerator ublIDGenerator = ublUtil.getIDGenerator(DocumentType.DEBIT_NOTE);
            if (ublIDGenerator == null) {
                throw new ModelException("Could not find a valid " + UBLIDGenerator.class.getSimpleName() + " for type[" + DocumentType.DEBIT_NOTE.toString() + "]");
            }

            String newDocumentId = ublIDGenerator.generateID(organization, debitNoteType);
            documentId = new IDType(newDocumentId);
            debitNoteType.setID(documentId);
        }

        DocumentModel document = documentManager.addDocument(organization, debitNoteType.getIDValue(), DocumentType.DEBIT_NOTE, debitNoteType);
        eventStoreManager.send(organization, getAdminEvent(organization)
                .operation(OperationType.CREATE)
                .resourcePath(uriInfo, document.getId())
                //.representation(debitNoteType)
                .getEvent());

        return document;
    }

    @POST
    @Path("/upload/invoices")
    @Consumes("multipart/form-data")
    @Produces(MediaType.APPLICATION_JSON)
    public void uploadInvoice(@PathParam("organization") final String organizationName, final MultipartFormDataInput input) throws ModelErrorResponseException {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireManage();

        List inputParts = getInputs(input);
        for (InputPart inputPart : inputParts) {
            try {
                InvoiceType invoiceType = readFile(inputPart, DocumentType.INVOICE, InvoiceType.class);
                uploadInvoice(organization, invoiceType);
            } catch (IOException e) {
                logger.error("Error reading input data", e);
                throw new ModelErrorResponseException("Error Reading data", Response.Status.BAD_REQUEST);
            } catch (ModelDuplicateException e) {
                throw new ModelErrorResponseException("Invoice exists with same documentId");
            } catch (ModelException e) {
                throw new ModelErrorResponseException("Could not create document");
            }
        }
    }

    @POST
    @Path("/upload/credit-notes")
    @Consumes("multipart/form-data")
    @Produces(MediaType.APPLICATION_JSON)
    public void uploadCreditNote(@PathParam("organization") final String organizationName, final MultipartFormDataInput input) throws ModelErrorResponseException {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireManage();

        List inputParts = getInputs(input);
        for (InputPart inputPart : inputParts) {
            try {
                CreditNoteType creditNoteType = readFile(inputPart, DocumentType.CREDIT_NOTE, CreditNoteType.class);

                uploadCreditNote(organization, creditNoteType);
            } catch (IOException e) {
                logger.error("Error reading input data", e);
                throw new ModelErrorResponseException("Error Reading data", Response.Status.BAD_REQUEST);
            } catch (ModelDuplicateException e) {
                throw new ModelErrorResponseException("Credit Note exists with same documentId");
            } catch (ModelException e) {
                throw new ModelErrorResponseException("Could not create document");
            }
        }
    }

    @POST
    @Path("/upload/debit-notes")
    @Consumes("multipart/form-data")
    @Produces(MediaType.APPLICATION_JSON)
    public void uploadDebitNote(@PathParam("organization") final String organizationName, final MultipartFormDataInput input) throws ModelErrorResponseException {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireManage();

        List inputParts = getInputs(input);
        for (InputPart inputPart : inputParts) {
            try {
                DebitNoteType debitNoteType = readFile(inputPart, DocumentType.DEBIT_NOTE, DebitNoteType.class);

                uploadDebitNote(organization, debitNoteType);
            } catch (IOException e) {
                logger.error("Error reading input data", e);
                throw new ModelErrorResponseException("Error Reading data", Response.Status.BAD_REQUEST);
            } catch (ModelDuplicateException e) {
                throw new ModelErrorResponseException("Debit Note exists with same documentId");
            } catch (ModelException e) {
                throw new ModelErrorResponseException("Could not create document");
            }
        }
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public SearchResultsRepresentation getDocuments(
            @PathParam("organization") final String organizationName,
            @QueryParam("documentType") String documentType,
            @QueryParam("documentId") String documentId,
            @QueryParam("query") @DefaultValue("") String query,
            @QueryParam("requiredActions") final List requiredActions,
            @QueryParam("includeDisabled") @DefaultValue("false") final Boolean includeDisabled,
            @QueryParam("first") Integer firstResult,
            @QueryParam("max") Integer maxResults) {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireView();

        List documents;

        if (documentType != null && documentId != null) {
            DocumentModel document = documentProvider.getDocumentByTypeAndDocumentId(documentType, documentId, organization);
            documents = Arrays.asList(modelToRepresentation.toRepresentation(document, true));

            SearchResultsRepresentation result = new SearchResultsRepresentation<>();
            result.setItems(documents);
            result.setTotalSize(1);
            return result;
        } else {
            // Process params
            DocumentRequiredAction[] documentRequiredActions = requiredActions.stream()
                    .map(requiredAction -> DocumentRequiredAction.valueOf(requiredAction.toUpperCase()))
                    .toArray(DocumentRequiredAction[]::new);
            firstResult = firstResult != null ? firstResult : -1;
            maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;

            // Jql
            DocumentQuery jql = documentProvider
                    .createQuery(organization)
                    .requiredAction(documentRequiredActions)
                    .applyQuery(query);

            if (!includeDisabled) {
                jql.enabled(true);
            }

            DocumentQuery.ListEntityQuery listEntityQuery = jql.entityQuery().resultList();
            DocumentQuery.CountQuery countQuery = jql.countQuery();

            documents = listEntityQuery
                    .firstResult(firstResult)
                    .maxResults(maxResults)
                    .getResultList().stream()
                    .map(documentModel -> modelToRepresentation.toRepresentation(documentModel, false))
                    .collect(Collectors.toList());
            int totalSize = countQuery.getTotalCount();

            SearchResultsRepresentation result = new SearchResultsRepresentation<>();
            result.setItems(documents);
            result.setTotalSize(totalSize);
            return result;
        }
    }

//    @GET
//    @Produces(MediaType.APPLICATION_JSON)
//    public SearchResultsRepresentation getDocuments(
//            @PathParam("organization") final String organizationName,
//            @QueryParam("filterText") String filterText,
//            @QueryParam("documentType") String documentType,
//            @QueryParam("documentId") String documentId,
//            @QueryParam("first") Integer firstResult,
//            @QueryParam("max") Integer maxResults) {
//        OrganizationModel organization = getOrganizationModel(organizationName);
//        OrganizationAuth auth = getAuth(organization);
//
//        auth.requireView();
//
//        firstResult = firstResult != null ? firstResult : -1;
//        maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
//
//        List documentModels;
//        if (filterText != null) {
//            documentModels = documentProvider.searchForDocument(filterText.trim(), organization, firstResult, maxResults);
//        } else if (documentId != null || documentType != null) {
//            Map attributes = new HashMap<>();
//            if (documentType != null) {
//                attributes.put(DocumentModel.DOCUMENT_TYPE, documentType);
//            }
//            if (documentId != null) {
//                attributes.put(DocumentModel.DOCUMENT_ID, documentId);
//            }
//            documentModels = documentProvider.createQuery(organization)
//                    .addFilter(attributes)
//                    .entityQuery()
//                    .resultList().firstResult(firstResult).maxResults(maxResults).getResultList();
//        } else {
//            documentModels = documentProvider.getDocuments(organization, firstResult, maxResults);
//        }
//
//        return documentModels.stream()
//                .map(f -> modelToRepresentation.toRepresentation(f))
//                .collect(Collectors.toList());
//    }

    @POST
    @Path("search")
    @Produces(MediaType.APPLICATION_JSON)
    public SearchResultsRepresentation search(@PathParam("organization") final String organizationName, final SearchCriteriaRepresentation criteria) {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireView();

        SearchCriteriaModel criteriaModel = representationToModel.toModel(criteria);
        String filterText = criteria.getFilterText();

        DocumentQuery query = documentProvider.createQuery(organization);

        // Filtertext
        if (filterText != null && !filterText.trim().isEmpty()) {
            query.filterText(filterText);
        }

        // Filters
        if (criteriaModel.getFilters() != null && !criteriaModel.getFilters().isEmpty()) {
            for (SearchCriteriaFilterModel filter : criteriaModel.getFilters()) {
                if (filter.getName().equalsIgnoreCase(DocumentModel.REQUIRED_ACTIONS)) {
                    List requiredActions = new ArrayList<>();
                    if (filter.getValue() instanceof String) {
                        requiredActions.add((String) filter.getValue());
                    } else if (filter.getValue() instanceof Collection) {
                        requiredActions.addAll((Collection) filter.getValue());
                    } else {
                        requiredActions.add(String.valueOf(filter.getValue()));
                    }

                    DocumentRequiredAction[] array = requiredActions
                            .stream()
                            .map(f -> DocumentRequiredAction.valueOf(f.toUpperCase()))
                            .toArray(size -> new DocumentRequiredAction[requiredActions.size()]);

                    query.requiredAction(array);
                } else if (filter.getName().equalsIgnoreCase(DocumentModel.CREATED_TIMESTAMP)) {
                    if (filter.getOperator().equals(SearchCriteriaFilterOperator.gt)) {
                        query.fromDate((LocalDateTime) filter.getValue(), false);
                    } else if (filter.getOperator().equals(SearchCriteriaFilterOperator.gte)) {
                        query.fromDate((LocalDateTime) filter.getValue(), true);
                    } else if (filter.getOperator().equals(SearchCriteriaFilterOperator.lt)) {
                        query.toDate((LocalDateTime) filter.getValue(), false);
                    } else if (filter.getOperator().equals(SearchCriteriaFilterOperator.lte)) {
                        query.toDate((LocalDateTime) filter.getValue(), true);
                    } else if (filter.getOperator().equals(SearchCriteriaFilterOperator.eq)) {
                        query.fromDate((LocalDateTime) filter.getValue(), true);
                        query.toDate((LocalDateTime) filter.getValue(), true);
                    } else {
                        throw new BadRequestException("Bad operator on criteria");
                    }
                } else {
                    query.addFilter(filter.getName(), filter.getValue(), filter.getOperator() != null ?  filter.getOperator() : SearchCriteriaFilterOperator.eq);
                }
            }
        }

        DocumentQuery.EntityQuery entityQuery = query.entityQuery();
        if (criteriaModel.getOrders() != null && !criteriaModel.getOrders().isEmpty()) {
            criteriaModel.getOrders().forEach(c -> {
                if (c.isAscending()) {
                    entityQuery.orderByAsc(c.getName());
                } else {
                    entityQuery.orderByDesc(c.getName());
                }
            });
        }

        SearchResultsModel results = entityQuery.searchResult().getSearchResult(criteriaModel.getPaging());

        SearchResultsRepresentation rep = new SearchResultsRepresentation<>();
        List items = new ArrayList<>();
        results.getModels().forEach(f -> items.add(modelToRepresentation.toRepresentation(f, false)));
        rep.setItems(items);
        rep.setTotalSize(results.getTotalSize());
        return rep;
    }


    /**
     * Get the document with the specified documentId.
     *
     * @return The document with the specified documentId
     */
    @GET
    @Path("/{document}")
    @Produces(MediaType.APPLICATION_JSON)
    public DocumentRepresentation getDocument(@PathParam("organization") final String organizationName,
                                              @PathParam("document") final String documentIdPk) {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireView();

        DocumentModel document = getDocument(documentIdPk, organization);
        return modelToRepresentation.toRepresentation(document, true);
    }

    @GET
    @Path("/{document}/representation/json")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getDocumentAsJson(@PathParam("organization") final String organizationName,
                                      @PathParam("document") final String documentIdPk) {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireView();

        DocumentModel document = getDocument(documentIdPk, organization);

        JSONObject jsonObject = document.getXmlAsJSONObject();
        if (jsonObject != null) {
            return Response.ok(jsonObject.toString()).build();
        } else {
            return ErrorResponse.exists("No json attached to current document");
        }
    }

    @GET
    @Path("/{document}/representation/xml")
    @Produces("application/xml")
    public Response getDebitNoteAsXml(@PathParam("organization") final String organizationName,
                                      @PathParam("document") final String documentIdPk) {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireView();

        DocumentModel document = getDocument(documentIdPk, organization);
        Document xml = document.getXmlAsDocument();
        return Response.ok(xml).build();
    }

    /**
     * Get the document report with the specified documentId.
     *
     * @return The byte[] with the specified documentId
     */
    @GET
    @Path("/{document}/report")
    public Response getPdf(
            @PathParam("organization") final String organizationName,
            @PathParam("document") final String documentIdPk,
            @QueryParam("theme") String theme,
            @QueryParam("format") @DefaultValue("pdf") String format) {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireView();

        DocumentModel document = getDocument(documentIdPk, organization);

        ExportFormat exportFormat = ExportFormat.valueOf(format.toUpperCase());
        try {
            ReportTemplateConfiguration reportConfig = new ReportTemplateConfiguration(organization).themeName(theme);
            byte[] reportBytes = reportTemplate.getReport(reportConfig, document, exportFormat);

            Response.ResponseBuilder response = Response.ok(reportBytes);
            switch (exportFormat) {
                case PDF:
                    response.type("application/pdf");
                    response.header("Content-Disposition", "attachment; filename=\"" + document.getDocumentId() + ".pdf\"");
                    break;
                case HTML:
                    response.type("application/html");
                    break;
            }

            return response.build();
        } catch (ReportException e) {
            return ErrorResponse.error("Error generating report", Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @PUT
    @Path("/{document}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response updateDocument(@PathParam("organization") final String organizationName,
                                   @PathParam("document") final String documentIdPk,
                                   @Valid final DocumentRepresentation rep) throws ModelErrorResponseException {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireManage();

        try {
            DocumentModel document = getDocument(documentIdPk, organization);

            Set actionsToRemove;
            if (rep.getRequiredActions() != null) {
                actionsToRemove = new HashSet<>(document.getRequiredActions());
                actionsToRemove.removeAll(rep.getRequiredActions());
            } else {
                actionsToRemove = Collections.emptySet();
            }

            updateDocumentFromRep(document, rep, actionsToRemove);
            eventStoreManager.send(organization, getAdminEvent(organization)
                    .operation(OperationType.UPDATE)
                    .resourcePath(uriInfo)
                    .representation(rep)
                    .getEvent());

            return Response.noContent().build();
        } catch (ModelDuplicateException e) {
            throw new ModelErrorResponseException("Document exists with same documentId or id", Response.Status.CONFLICT);
        } catch (ModelException me) {
            logger.warn("Could not update document!", me);
            throw new ModelErrorResponseException("Could not update document!");
        }
    }

    private static void updateDocumentFromRep(DocumentModel document, DocumentRepresentation rep, Set actionsToRemove) throws ModelException {
        for (String action : actionsToRemove) {
            document.removeRequiredAction(action);
        }

        if (rep.getRequiredActions() != null) {
            Set newActions = rep.getRequiredActions();
            newActions.removeAll(document.getRequiredActions());
            for (String action : newActions) {
                document.addRequiredAction(action);
            }
        }
    }

    /**
     * Deletes document with given documentId.
     */
    @DELETE
    @Path("/{document}")
    public Response deleteDocument(@PathParam("organization") final String organizationName,
                                   @PathParam("document") final String documentIdPk) throws ModelErrorResponseException {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireManage();

        DocumentModel document = getDocument(documentIdPk, organization);

        boolean removed = documentManager.removeDocument(organization, document);
        if (removed) {
            eventStoreManager.send(organization, getAdminEvent(organization)
                    .operation(OperationType.DELETE)
                    .resourcePath(uriInfo)
                    .getEvent());
            return Response.noContent().build();
        } else {
            throw new ModelErrorResponseException("Document couldn't be deleted", Response.Status.BAD_REQUEST);
        }
    }

    @POST
    @Path("/{document}/send-to-customer")
    @Produces(MediaType.APPLICATION_JSON)
    public Response sendToCustomer(@PathParam("organization") final String organizationName,
                                   @PathParam("document") final String documentIdPk) throws ModelErrorResponseException {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireManage();

        DocumentModel document = getDocument(documentIdPk, organization);

        SendEventModel sendEvent = sendDocument(organization, document, DestinyType.CUSTOMER, null);
        return Response.ok(modelToRepresentation.toRepresentation(sendEvent)).build();
    }

    @POST
    @Path("/{document}/send-to-third-party")
    @Produces(MediaType.APPLICATION_JSON)
    public Response sendToThirdParty(@PathParam("organization") final String organizationName,
                                     @PathParam("document") final String documentIdPk) throws ModelErrorResponseException {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireManage();

        DocumentModel document = getDocument(documentIdPk, organization);

        SendEventModel sendEvent = sendDocument(organization, document, DestinyType.THIRD_PARTY, null);
        return Response.ok(modelToRepresentation.toRepresentation(sendEvent)).build();
    }

    @POST
    @Path("/{document}/send-to-third-party-by-email")
    @Produces(MediaType.APPLICATION_JSON)
    public void sendToThirdPartyByEmail(@PathParam("organization") final String organizationName,
                                        @PathParam("document") final String documentIdPk,
                                        ThirdPartyEmailRepresentation thirdParty) throws ModelErrorResponseException {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireManage();

        DocumentModel document = getDocument(documentIdPk, organization);
        if (thirdParty == null || thirdParty.getEmail() == null) {
            throw new BadRequestException("Invalid email sended");
        }

        sendDocument(organization, document, DestinyType.CUSTOM_EMAIL, thirdParty.getEmail());
//        SendEventModel sendEvent = sendDocument(organization, document, DestinyType.CUSTOM_EMAIL, thirdParty.getEmail());
//        return Response.ok(modelToRepresentation.toRepresentation(sendEvent)).build();
    }

    private SendEventModel sendDocument(OrganizationModel organization, DocumentModel document, DestinyType destiny, String customEmail) throws ModelErrorResponseException {
        SendEventModel sendEvent;
        try {
            switch (destiny) {
                case CUSTOMER:
                    sendEvent = documentManager.sendToCustomerParty(organization, document);
                    break;
                case THIRD_PARTY:
                    sendEvent = documentManager.sendToThirdParty(organization, document);
                    break;
                case CUSTOM_EMAIL:
                    sendEvent = documentManager.sendToCustomEmail(organization, document, customEmail);
                    break;
                default:
                    throw new IllegalStateException("Invalid destiny[" + destiny + "] type");
            }
        } catch (ModelInsuficientData | SendEventException e) {
            sendEvent = document.addSendEvent(destiny);
            sendEvent.setResult(SendEventStatus.ERROR);
            if (e.getMessage() != null) {
                sendEvent.setDescription(e.getMessage().length() < 200 ? e.getMessage() : e.getMessage().substring(0, 197).concat("..."));
            }
            logger.error(e.getMessage(), e);
        }
        return sendEvent;
    }

    @GET
    @Path("/{document}/send-events")
    @Produces(MediaType.APPLICATION_JSON)
    public List getSendEvents(
            @PathParam("organization") final String organizationName,
            @PathParam("document") final String documentIdPk,
            @QueryParam("destinyType") String destinyType,
            @QueryParam("result") String result,
            @QueryParam("first") Integer firstResult,
            @QueryParam("max") Integer maxResults) {
        OrganizationModel organization = getOrganizationModel(organizationName);
        OrganizationAuth auth = getAuth(organization);

        auth.requireView();

        DocumentModel document = getDocument(documentIdPk, organization);

        firstResult = firstResult != null ? firstResult : -1;
        maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;

        List sendEventModels;
        if (destinyType != null || result != null) {
            Map attributes = new HashMap<>();
            if (destinyType != null) {
                attributes.put(DocumentModel.SEND_EVENT_DESTINY, destinyType);
            }
            if (result != null) {
                attributes.put(DocumentModel.SEND_EVENT_STATUS, result);
            }
            sendEventModels = document.searchForSendEvent(attributes, firstResult, maxResults);
        } else {
            sendEventModels = document.getSendEvents(firstResult, maxResults);
        }

        return sendEventModels.stream()
                .map(f -> modelToRepresentation.toRepresentation(f))
                .collect(Collectors.toList());
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy