Your IP : 216.73.217.95


Current Path : /home/deltalab/PMS/partner-manager-backend/services/
Upload File :
Current File : //home/deltalab/PMS/partner-manager-backend/services/billing.js

/* eslint-disable new-cap */
/* eslint-disable no-await-in-loop */
/* eslint-disable no-use-before-define */
/**
 * Billing service
 * Builds up invoices and billings to be inserted in the DB
 */

const { invoiceModel, royaltyModel, warehouseCostModel } = require('../models/mongoose/invoice');
const { partnerModel } = require('../models/mongoose/partner');
const { orderModel } = require('../models/mongoose/order');
const { subscriptionPlanModel } = require('../models/mongoose/subscription-plan');
const { indacoModuleModel } = require('../models/mongoose/indaco-module');
const { warehouseModel } = require('../models/mongoose/warehouse');
const { warehouseJournalModel } = require('../models/mongoose/warehouse-journal');

async function createInvoice(invoicePId, startDate, endDate) {
  const invoice = new invoiceModel();

  // Filling the common fields
  invoice.partnerId = invoicePId;
  invoice.startDate = startDate;
  invoice.endDate = endDate;

  // Find the partner's financial details
  const partner = await partnerModel.findById(invoicePId);
  if (!partner) {
    return { error: 'No partner with the specified Id found' };
  }
  invoice.companyName = partner.companyName;
  invoice.companyAddress = partner.address;
  invoice.partnerVatNumber = partner.vatNumber;

  // Fill in the INDACO
  invoice.indacoCompanyName = 'INDACO SpA';
  invoice.indacoVatNumber = 'Some VAT number';
  invoice.indacoAddress = 'Via Kufstein 5, Trento';

  // Fetch and fill all the orders for the specified partner
  const query = {};
  query.partnerId = invoicePId;
  query.fullyPaid = true;
  query.createdAt = {
    $gte: startDate,
    $lt: endDate,
  };
  const orders = await orderModel.find(query);
  invoice.orders = orders;

  // Fetch the subscription plan and calculate the costs
  const partnerPlan = await subscriptionPlanModel.findById(partner.subscriptionPlanId);
  const subscriptionModules = await indacoModuleModel.find({ '_id': { $in: partnerPlan.indacoModulesIds } });

  subscriptionModules.forEach((module) => {
    invoice.services.push(module);
  });

  // Calculate all the royalties for the orders based on the plan
  orders.forEach((order) => {
    const royalty = new royaltyModel();
    royalty.orderId = order.omsgid;
    royalty.royaltyAmount = (order.totalPriceSet.amount.valueOf() * partnerPlan.royaltyRate.valueOf()) / 100;
    royalty.currency = order.totalPriceSet.currencyCode;
    invoice.royalties.push(royalty);
  });

  const sharedWarehouses = await warehouseModel.find({ isShared: true });
  for (let i = 0; i < sharedWarehouses.length; i++) {
    for (let j = 0; j < sharedWarehouses[i].assignments.length; j++) {
      if (sharedWarehouses[i].assignments[j].partnerId === invoicePId) {
        invoice.warehouseCosts.push(await generateWarehouseInboundCost(sharedWarehouses[i].assignments[j], sharedWarehouses[i], invoicePId, startDate, endDate));
        invoice.warehouseCosts.push(await generateWarehouseStorageCost(sharedWarehouses[i].assignments[j], sharedWarehouses[i]));
        invoice.warehouseCosts.push(await generateWarehouseOrderCost(sharedWarehouses[i].assignments[j], sharedWarehouses[i], startDate, endDate));
      }
    }
  }

  return invoice;
}

async function generateWarehouseInboundCost(assignment, warehouse, partnerId, startDate, endDate) {
  const journalEntries = await warehouseJournalModel.find({
    $and: [
      { partnerId },
      { $or: [{ reason: 'INITIAL' }, { reason: 'INCREASE' }] },
      { date: { $gt: startDate } },
      { date: { $lt: endDate } },
      { warehouseId: warehouse._id },
      { refrigerated: assignment.refrigerated },
    ],
  });

  const warehouseInboundCost = new warehouseCostModel();
  warehouseInboundCost.costObject = 'INBOUND';
  warehouseInboundCost.warehouseId = warehouse._id;
  warehouseInboundCost.warehouseName = warehouse.name;
  warehouseInboundCost.rate = assignment.inboundRate;
  warehouseInboundCost.total = warehouseInboundCost.rate * journalEntries.length;
  warehouseInboundCost.currency = 'EUR';
  return warehouseInboundCost;
}

async function generateWarehouseStorageCost(assignment, warehouse) {
  const warehouseStorageCost = new warehouseCostModel();
  warehouseStorageCost.costObject = 'STORAGE';
  warehouseStorageCost.warehouseId = warehouse._id;
  warehouseStorageCost.warehouseName = warehouse.name;
  warehouseStorageCost.rate = assignment.storageRate;
  warehouseStorageCost.total = warehouseStorageCost.rate * assignment.minimumStorage;
  warehouseStorageCost.currency = 'EUR';
  return warehouseStorageCost;
}

async function generateWarehouseOrderCost(assignment, warehouse, startDate, endDate) {
  const journalEntries = await warehouseJournalModel.find({
    $and: [
      { partnerId: assignment.partnerId },
      { reason: 'ORDER' },
      { date: { $gt: startDate } },
      { date: { $lt: endDate } },
      { warehouseId: warehouse._id },
      { refrigerated: assignment.refrigerated },
    ],
  });

  const warehouseOrderCost = new warehouseCostModel();
  warehouseOrderCost.costObject = 'ORDER';
  warehouseOrderCost.warehouseId = warehouse._id;
  warehouseOrderCost.warehouseName = warehouse.name;
  warehouseOrderCost.rate = assignment.orderRate;
  warehouseOrderCost.total = warehouseOrderCost.rate * journalEntries.length;
  warehouseOrderCost.currency = 'EUR';
  return warehouseOrderCost;
}

async function fetchInvoicesPeriod(partnerId, startDate, endDate) {
  const query = {};
  query.partnerId = partnerId;
  query.createdAt = {
    $gte: startDate,
    $lt: endDate,
  };
  const invoices = await invoiceModel.find(query);

  return invoices;
}

module.exports = {
  createInvoice,
  fetchInvoicesPeriod,
};