Skip to content

Commit

Permalink
[MODAUD-196]. Implement consumer & endpoint for invoice line records
Browse files Browse the repository at this point in the history
  • Loading branch information
BKadirkhodjaev committed Nov 4, 2024
1 parent 727953c commit 85d0446
Show file tree
Hide file tree
Showing 23 changed files with 832 additions and 3 deletions.
23 changes: 22 additions & 1 deletion descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,21 @@
}
]
},
{
"id": "acquisition-invoice-line-events",
"version": "1.0",
"handlers": [
{
"methods": [
"GET"
],
"pathPattern": "/audit-data/acquisition/invoice-line/{id}",
"permissionsRequired": [
"acquisition.invoice-line.events.get"
]
}
]
},
{
"id": "audit-data-event-handlers",
"version": "1.1",
Expand Down Expand Up @@ -264,6 +279,11 @@
"displayName": "Acquisition piece status change history events - get piece status change events",
"description": "Get piece status change events"
},
{
"permissionName": "acquisition.invoice-line.events.get",
"displayName": "Acquisition invoice-line events - get invoice-line change events",
"description": "Get invoice-line change events"
},
{
"permissionName": "audit.all",
"displayName": "Audit - all permissions",
Expand All @@ -278,7 +298,8 @@
"acquisition.order.events.get",
"acquisition.order-line.events.get",
"acquisition.piece.events.get",
"acquisition.piece.events.history.get"
"acquisition.piece.events.history.get",
"acquisition.invoice-line.events.get"
]
}
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.folio.dao.acquisition;

import io.vertx.core.Future;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent;
import org.folio.rest.jaxrs.model.InvoiceLineAuditEventCollection;

public interface InvoiceLineEventsDao {

/**
* Saves invoiceLineAuditEvent entity to DB
*
* @param invoiceLineAuditEvent InvoiceLineAuditEvent entity to save
* @param tenantId tenant id
* @return future with created row
*/
Future<RowSet<Row>> save(InvoiceLineAuditEvent invoiceLineAuditEvent, String tenantId);

/**
* Searches for invoice_line audit events by id
*
* @param invoiceLineId invoice_line id
* @param sortBy sort by
* @param sortOrder sort order
* @param limit limit
* @param offset offset
* @param tenantId tenant id
* @return future with InvoiceLineAuditEventCollection
*/
Future<InvoiceLineAuditEventCollection> getAuditEventsByInvoiceLineId(String invoiceLineId, String sortBy, String sortOrder, int limit, int offset, String tenantId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package org.folio.dao.acquisition.impl;

import static java.lang.String.format;
import static org.folio.util.AuditEventDBConstants.ACTION_DATE_FIELD;
import static org.folio.util.AuditEventDBConstants.ACTION_FIELD;
import static org.folio.util.AuditEventDBConstants.EVENT_DATE_FIELD;
import static org.folio.util.AuditEventDBConstants.ID_FIELD;
import static org.folio.util.AuditEventDBConstants.INVOICE_ID_FIELD;
import static org.folio.util.AuditEventDBConstants.INVOICE_LINE_ID_FIELD;
import static org.folio.util.AuditEventDBConstants.MODIFIED_CONTENT_FIELD;
import static org.folio.util.AuditEventDBConstants.ORDER_BY_PATTERN;
import static org.folio.util.AuditEventDBConstants.TOTAL_RECORDS_FIELD;
import static org.folio.util.AuditEventDBConstants.USER_ID_FIELD;
import static org.folio.util.DbUtils.formatDBTableName;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.UUID;

import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.json.JsonObject;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
import io.vertx.sqlclient.Tuple;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.dao.acquisition.InvoiceLineEventsDao;
import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent;
import org.folio.rest.jaxrs.model.InvoiceLineAuditEventCollection;
import org.folio.util.PostgresClientFactory;
import org.springframework.stereotype.Repository;

@Repository
public class InvoiceLineEventsDaoImpl implements InvoiceLineEventsDao {

private static final Logger LOGGER = LogManager.getLogger();

public static final String TABLE_NAME = "acquisition_invoice_line_log";

public static final String GET_BY_INVOICE_LINE_ID_SQL = "SELECT id, action, invoice_id, invoice_line_id, user_id, event_date, action_date, modified_content_snapshot," +
" (SELECT count(*) AS total_records FROM %s WHERE invoice_line_id = $1) " +
" FROM %s WHERE invoice_line_id = $1 %s LIMIT $2 OFFSET $3";

private static final String INSERT_SQL = "INSERT INTO %s (id, action, invoice_id, invoice_line_id, user_id, event_date, action_date, modified_content_snapshot) " +
"VALUES ($1, $2, $3, $4, $5, $6, $7, $8)";

private final PostgresClientFactory pgClientFactory;

public InvoiceLineEventsDaoImpl(PostgresClientFactory pgClientFactory) {
this.pgClientFactory = pgClientFactory;
}

@Override
public Future<RowSet<Row>> save(InvoiceLineAuditEvent invoiceLineAuditEvent, String tenantId) {
LOGGER.debug("save:: Saving InvoiceLine AuditEvent with tenant id : {}", tenantId);
Promise<RowSet<Row>> promise = Promise.promise();
LOGGER.debug("formatDBTableName:: Formatting DB Table Name with tenant id : {}", tenantId);
String logTable = formatDBTableName(tenantId, TABLE_NAME);
String query = format(INSERT_SQL, logTable);
makeSaveCall(promise, query, invoiceLineAuditEvent, tenantId);
LOGGER.info("save:: Saved InvoiceLine AuditEvent with tenant id : {}", tenantId);
return promise.future();
}

@Override
public Future<InvoiceLineAuditEventCollection> getAuditEventsByInvoiceLineId(String invoiceLineId, String sortBy, String sortOrder, int limit, int offset, String tenantId) {
LOGGER.debug("getAuditEventsByInvoiceLineId:: Retrieving AuditEvent with invoice line id : {}", invoiceLineId);
Promise<RowSet<Row>> promise = Promise.promise();
try {
LOGGER.debug("formatDBTableName:: Formatting DB Table Name with tenant id : {}", tenantId);
String logTable = formatDBTableName(tenantId, TABLE_NAME);
String query = format(GET_BY_INVOICE_LINE_ID_SQL, logTable, logTable, format(ORDER_BY_PATTERN, sortBy, sortOrder));
Tuple queryParams = Tuple.of(UUID.fromString(invoiceLineId), limit, offset);
pgClientFactory.createInstance(tenantId).selectRead(query, queryParams, promise);
} catch (Exception e) {
LOGGER.warn("Error getting invoice line audit events by invoice line id: {}", invoiceLineId, e);
promise.fail(e);
}

LOGGER.info("getAuditEventsByInvoiceLineId:: Retrieved AuditEvent with invoice line id : {}", invoiceLineId);
return promise.future().map(rowSet -> rowSet.rowCount() == 0 ? new InvoiceLineAuditEventCollection().withTotalItems(0)
: mapRowToListOfInvoiceLineEvent(rowSet));
}

private void makeSaveCall(Promise<RowSet<Row>> promise, String query, InvoiceLineAuditEvent invoiceLineAuditEvent, String tenantId) {
LOGGER.debug("makeSaveCall:: Making save call with query : {} and tenant id : {}", query, tenantId);
try {
pgClientFactory.createInstance(tenantId).execute(query, Tuple.of(invoiceLineAuditEvent.getId(),
invoiceLineAuditEvent.getAction(),
invoiceLineAuditEvent.getInvoiceId(),
invoiceLineAuditEvent.getInvoiceLineId(),
invoiceLineAuditEvent.getUserId(),
LocalDateTime.ofInstant(invoiceLineAuditEvent.getEventDate().toInstant(), ZoneId.systemDefault()),
LocalDateTime.ofInstant(invoiceLineAuditEvent.getActionDate().toInstant(), ZoneId.systemDefault()),
JsonObject.mapFrom(invoiceLineAuditEvent.getInvoiceLineSnapshot())), promise);
} catch (Exception e) {
LOGGER.error("Failed to save record with id: {} for invoice line id: {} in to table {}",
invoiceLineAuditEvent.getId(), invoiceLineAuditEvent.getInvoiceLineId(), TABLE_NAME, e);
promise.fail(e);
}
}

private InvoiceLineAuditEventCollection mapRowToListOfInvoiceLineEvent(RowSet<Row> rowSet) {
LOGGER.debug("mapRowToListOfInvoiceLineEvent:: Mapping row to List of Invoice Line Events");
InvoiceLineAuditEventCollection invoiceLineAuditEventCollection = new InvoiceLineAuditEventCollection();
rowSet.iterator().forEachRemaining(row -> {
invoiceLineAuditEventCollection.getInvoiceLineAuditEvents().add(mapRowToInvoiceLineEvent(row));
invoiceLineAuditEventCollection.setTotalItems(row.getInteger(TOTAL_RECORDS_FIELD));
});
LOGGER.debug("mapRowToListOfInvoiceLineEvent:: Mapped row to List of Invoice Line Events");
return invoiceLineAuditEventCollection;
}

private InvoiceLineAuditEvent mapRowToInvoiceLineEvent(Row row) {
LOGGER.debug("mapRowToInvoiceLineEvent:: Mapping row to Invoice Line Event");
return new InvoiceLineAuditEvent()
.withId(row.getValue(ID_FIELD).toString())
.withAction(row.get(InvoiceLineAuditEvent.Action.class, ACTION_FIELD))
.withInvoiceId(row.getValue(INVOICE_ID_FIELD).toString())
.withInvoiceLineId(row.getValue(INVOICE_LINE_ID_FIELD).toString())
.withUserId(row.getValue(USER_ID_FIELD).toString())
.withEventDate(Date.from(row.getLocalDateTime(EVENT_DATE_FIELD).toInstant(ZoneOffset.UTC)))
.withActionDate(Date.from(row.getLocalDateTime(ACTION_DATE_FIELD).toInstant(ZoneOffset.UTC)))
.withInvoiceLineSnapshot(JsonObject.mapFrom(row.getValue(MODIFIED_CONTENT_FIELD)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
import io.vertx.core.Future;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.rest.jaxrs.model.AuditDataAcquisitionInvoiceLineIdGetSortOrder;
import org.folio.rest.jaxrs.model.AuditDataAcquisitionOrderIdGetSortOrder;
import org.folio.rest.jaxrs.model.AuditDataAcquisitionOrderLineIdGetSortOrder;
import org.folio.rest.jaxrs.model.AuditDataAcquisitionPieceIdGetSortOrder;
import org.folio.rest.jaxrs.model.AuditDataAcquisitionPieceIdStatusChangeHistoryGetSortOrder;
import org.folio.rest.jaxrs.resource.AuditDataAcquisition;
import org.folio.rest.tools.utils.TenantTool;
import org.folio.services.acquisition.InvoiceLineAuditEventsService;
import org.folio.services.acquisition.OrderAuditEventsService;
import org.folio.services.acquisition.OrderLineAuditEventsService;
import org.folio.services.acquisition.PieceAuditEventsService;
Expand All @@ -35,6 +37,8 @@ public class AuditDataAcquisitionImpl implements AuditDataAcquisition {
private OrderLineAuditEventsService orderLineAuditEventsService;
@Autowired
private PieceAuditEventsService pieceAuditEventsService;
@Autowired
private InvoiceLineAuditEventsService invoiceLineAuditEventsService;

public AuditDataAcquisitionImpl() {
SpringContextUtil.autowireDependencies(this, Vertx.currentContext());
Expand Down Expand Up @@ -110,6 +114,23 @@ public void getAuditDataAcquisitionPieceStatusChangeHistoryById(String pieceId,
}
}

@Override
public void getAuditDataAcquisitionInvoiceLineById(String invoiceLineId, String sortBy, AuditDataAcquisitionInvoiceLineIdGetSortOrder sortOrder,
int limit, int offset, Map<String, String> okapiHeaders, Handler<AsyncResult<Response>> asyncResultHandler, Context vertxContext) {
LOGGER.debug("getAuditDataAcquisitionInvoiceLineById:: Retrieving Audit Data Acquisition Invoice Line By Id : {}", invoiceLineId);
String tenantId = TenantTool.tenantId(okapiHeaders);
try {
invoiceLineAuditEventsService.getAuditEventsByInvoiceLineId(invoiceLineId, sortBy, sortOrder.name(), limit, offset, tenantId)
.map(GetAuditDataAcquisitionInvoiceLineByIdResponse::respond200WithApplicationJson)
.map(Response.class::cast)
.otherwise(this::mapExceptionToResponse)
.onComplete(asyncResultHandler);
} catch (Exception e) {
LOGGER.error("Failed to get order line audit events by order line id: {}", invoiceLineId, e);
asyncResultHandler.handle(Future.succeededFuture(mapExceptionToResponse(e)));
}
}

private Response mapExceptionToResponse(Throwable throwable) {
LOGGER.debug("mapExceptionToResponse:: Mapping Exception :{} to Response", throwable.getMessage(), throwable);
return GetAuditDataAcquisitionOrderByIdResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.folio.rest.resource.interfaces.InitAPI;
import org.folio.spring.SpringContextUtil;
import org.folio.verticle.SpringVerticleFactory;
import org.folio.verticle.acquisition.InvoiceLineEventConsumersVerticle;
import org.folio.verticle.acquisition.OrderEventConsumersVerticle;
import org.folio.verticle.acquisition.OrderLineEventConsumersVerticle;
import org.folio.verticle.acquisition.PieceEventConsumersVerticle;
Expand Down Expand Up @@ -64,6 +65,7 @@ private Future<?> deployConsumersVerticles(Vertx vertx) {
Promise<String> orderEventsConsumer = Promise.promise();
Promise<String> orderLineEventsConsumer = Promise.promise();
Promise<String> pieceEventsConsumer = Promise.promise();
Promise<String> invoiceLineEventsConsumer = Promise.promise();

vertx.deployVerticle(getVerticleName(verticleFactory, OrderEventConsumersVerticle.class),
new DeploymentOptions()
Expand All @@ -80,11 +82,17 @@ private Future<?> deployConsumersVerticles(Vertx vertx) {
.setWorker(true)
.setInstances(acqPieceConsumerInstancesNumber), pieceEventsConsumer);

vertx.deployVerticle(getVerticleName(verticleFactory, InvoiceLineEventConsumersVerticle.class),
new DeploymentOptions()
.setWorker(true)
.setInstances(acqPieceConsumerInstancesNumber), invoiceLineEventsConsumer);

LOGGER.info("deployConsumersVerticles:: All consumer verticles were successfully deployed");
return GenericCompositeFuture.all(Arrays.asList(
orderEventsConsumer.future(),
orderLineEventsConsumer.future(),
pieceEventsConsumer.future()));
pieceEventsConsumer.future(),
invoiceLineEventsConsumer.future()));
}

private <T> String getVerticleName(VerticleFactory verticleFactory, Class<T> clazz) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.folio.services.acquisition;

import io.vertx.core.Future;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent;
import org.folio.rest.jaxrs.model.InvoiceLineAuditEventCollection;

public interface InvoiceLineAuditEventsService {

/**
* Saves InvoiceLineAuditEvent
*
* @param invoiceLineAuditEvent invoice line event to save
* @param tenantId id of tenant
* @return successful future if event has not been processed, or failed future otherwise
*/
Future<RowSet<Row>> saveInvoiceLineAuditEvent(InvoiceLineAuditEvent invoiceLineAuditEvent, String tenantId);

/**
* Searches for invoice line audit events by invoice line id
*
* @param invoiceLineId invoice line id
* @param sortBy sort by
* @param sortOrder sort order
* @param limit limit
* @param offset offset
* @return future with InvoiceLineAuditEventCollection
*/
Future<InvoiceLineAuditEventCollection> getAuditEventsByInvoiceLineId(String invoiceLineId, String sortBy, String sortOrder, int limit, int offset, String tenantId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.folio.services.acquisition.impl;

import static org.folio.util.ErrorUtils.handleFailures;

import io.vertx.core.Future;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.dao.acquisition.InvoiceLineEventsDao;
import org.folio.rest.jaxrs.model.InvoiceLineAuditEvent;
import org.folio.rest.jaxrs.model.InvoiceLineAuditEventCollection;
import org.folio.services.acquisition.InvoiceLineAuditEventsService;
import org.springframework.stereotype.Service;

@Service
public class InvoiceLineAuditEventsServiceImpl implements InvoiceLineAuditEventsService {

private static final Logger LOGGER = LogManager.getLogger();

private final InvoiceLineEventsDao invoiceLineEventsDao;

public InvoiceLineAuditEventsServiceImpl(InvoiceLineEventsDao invoiceLineEventsDao) {
this.invoiceLineEventsDao = invoiceLineEventsDao;
}

@Override
public Future<RowSet<Row>> saveInvoiceLineAuditEvent(InvoiceLineAuditEvent invoiceLineAuditEvent, String tenantId) {
LOGGER.debug("saveInvoiceLineAuditEvent:: Saving invoice line audit event with id: {} in tenant Id : {}", invoiceLineAuditEvent.getId(), tenantId);
return invoiceLineEventsDao.save(invoiceLineAuditEvent, tenantId)
.recover(throwable -> {
LOGGER.error("handleFailures:: Could not save invoice audit event for InvoiceLine id: {} in tenantId: {}", invoiceLineAuditEvent.getInvoiceLineId(), tenantId);
return handleFailures(throwable, invoiceLineAuditEvent.getId());
});
}

@Override
public Future<InvoiceLineAuditEventCollection> getAuditEventsByInvoiceLineId(String invoiceLineId, String sortBy, String sortOrder, int limit, int offset, String tenantId) {
LOGGER.debug("getAuditEventsByInvoiceLineId:: Retrieving audit events for invoice line Id : {} and tenant Id : {}", invoiceLineId, tenantId);
return invoiceLineEventsDao.getAuditEventsByInvoiceLineId(invoiceLineId, sortBy, sortOrder, limit, offset, tenantId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
public enum AcquisitionEventType {
ACQ_ORDER_CHANGED("ACQ_ORDER_CHANGED"),
ACQ_ORDER_LINE_CHANGED("ACQ_ORDER_LINE_CHANGED"),
ACQ_PIECE_CHANGED("ACQ_PIECE_CHANGED");
ACQ_PIECE_CHANGED("ACQ_PIECE_CHANGED"),
ACQ_INVOICE_LINE_CHANGED("ACQ_INVOICE_LINE_CHANGED") ;

private final String topicName;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ private AuditEventDBConstants() {}

public static final String PIECE_ID_FIELD = "piece_id";

public static final String INVOICE_ID_FIELD = "invoice_id";

public static final String INVOICE_LINE_ID_FIELD = "invoice_line_id";

public static final String USER_ID_FIELD = "user_id";

public static final String EVENT_DATE_FIELD = "event_date";
Expand Down
Loading

0 comments on commit 85d0446

Please sign in to comment.