Moved the batch integrity report from settings to its own report in products with permission of product ledger.

It also automatically fixes the issue prices.
This commit is contained in:
Amritanshu Agrawal 2021-09-23 07:20:45 +05:30
parent b1557bef88
commit 6d0d95070b
27 changed files with 613 additions and 200 deletions

@ -13,6 +13,7 @@ from .routers import (
attendance_report,
attendance_types,
batch,
batch_integrity,
client,
cost_centre,
credit_salary,
@ -47,6 +48,7 @@ from .routers.reports import (
cash_flow,
closing_stock,
daybook,
entries,
ledger,
net_transactions,
product_ledger,
@ -57,7 +59,6 @@ from .routers.reports import (
reconcile,
stock_movement,
trial_balance,
entries,
)
@ -105,6 +106,7 @@ app.include_router(reconcile.router, prefix="/api/reconcile", tags=["reports"])
app.include_router(stock_movement.router, prefix="/api/stock-movement", tags=["reports"])
app.include_router(trial_balance.router, prefix="/api/trial-balance", tags=["reports"])
app.include_router(entries.router, prefix="/api/entries", tags=["reports"])
app.include_router(batch_integrity.router, prefix="/api/batch-integrity", tags=["reports"])
app.include_router(issue_grid.router, prefix="/api/issue-grid", tags=["vouchers"])
app.include_router(batch.router, prefix="/api/batch", tags=["vouchers"])

@ -21,7 +21,7 @@ class Batch(Base):
tax = Column("tax", Numeric(precision=15, scale=5), nullable=False)
discount = Column("discount", Numeric(precision=15, scale=5), nullable=False)
inventories = relationship("Inventory", backref="batch", cascade=None, cascade_backrefs=False)
inventories = relationship("Inventory", back_populates="batch")
product = relationship("Product", back_populates="batches")
def __init__(

@ -28,6 +28,7 @@ class Inventory(Base):
voucher = relationship("Voucher", back_populates="inventories")
product = relationship("Product", back_populates="inventories")
batch = relationship("Batch", back_populates="inventories")
def __init__(
self,

@ -0,0 +1,172 @@
import uuid
from typing import List
import brewman.schemas.batch_integrity as schemas
from fastapi import APIRouter, Security
from sqlalchemy import func, select, update
from sqlalchemy.orm import Session, contains_eager
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionFuture
from ..models.batch import Batch
from ..models.cost_centre import CostCentre
from ..models.inventory import Inventory
from ..models.journal import Journal
from ..models.product import Product
from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType
from ..schemas.user import UserToken
from .issue import refresh_voucher
router = APIRouter()
@router.post("", response_model=List[schemas.BatchIntegrity])
def post_check_batch_integrity(
user: UserToken = Security(get_user, scopes=["product-ledger"])
) -> List[schemas.BatchIntegrity]:
with SessionFuture() as db:
info = batches(db) + batch_dates(db)
fix_batch_prices(db)
db.commit()
return info
def batches(db: Session) -> List[schemas.BatchIntegrity]:
inv_sum = func.sum(Inventory.quantity * Journal.debit).label("quantity")
list_ = db.execute(
select(Batch, Product.full_name, inv_sum)
.join(Batch.product)
.join(Batch.inventories)
.join(Inventory.voucher)
.join(Voucher.journals)
.where(
Voucher.type.in_(
[
VoucherType.by_name("Purchase").id,
VoucherType.by_name("Purchase Return").id,
VoucherType.by_name("Issue").id,
VoucherType.by_name("Opening Batches").id,
]
),
Journal.cost_centre_id == CostCentre.cost_centre_purchase(),
)
.group_by(Batch, Product.full_name)
.having(Batch.quantity_remaining != inv_sum)
).all()
issue = []
for batch, product, quantity in list_:
if quantity >= 0:
db.execute(update(Batch).where(Batch.id == batch.id).values(quantity_remaining=quantity))
else:
issue.append(
schemas.BatchIntegrity(
id=batch.id,
product=product,
date=batch.name,
showing=batch.quantity_remaining,
actual=quantity,
price=batch.rate,
details=batch_details(batch.id, db),
)
)
return issue
def batch_details(batch_id: uuid.UUID, db: Session) -> List[schemas.BatchIntegrityItem]:
list_ = db.execute(
select(Voucher.id, Voucher.date, Voucher.type, Inventory.quantity, Inventory.rate)
.join(Inventory.voucher)
.join(Voucher.journals)
.where(
Inventory.batch_id == batch_id,
Voucher.type.in_(
[
VoucherType.by_name("Purchase").id,
VoucherType.by_name("Purchase Return").id,
VoucherType.by_name("Issue").id,
VoucherType.by_name("Opening Batches").id,
]
),
Journal.cost_centre_id == CostCentre.cost_centre_purchase(),
)
).all()
return [
schemas.BatchIntegrityItem(
date=date_,
type=VoucherType.by_id(type_).name,
url=["/", VoucherType.by_id(type_).name.replace(" ", "-").lower(), str(id_)],
quantity=quantity,
price=rate,
)
for id_, date_, type_, quantity, rate in list_
]
def batch_dates(db: Session) -> List[schemas.BatchIntegrity]:
list_ = (
db.execute(
select(Batch)
.join(Batch.product)
.join(Batch.inventories)
.join(Inventory.voucher)
.where(Voucher.date < Batch.name)
.options(contains_eager(Batch.product), contains_eager(Batch.inventories).contains_eager(Inventory.voucher))
)
.unique()
.scalars()
.all()
)
issue = []
for batch in list_:
issue.append(
schemas.BatchIntegrity(
id=batch.id,
product=batch.product.full_name,
date=batch.name,
showing=batch.quantity_remaining,
actual=0,
price=batch.rate,
details=[
schemas.BatchIntegrityItem(
date=inv.voucher.date,
type=VoucherType.by_id(inv.voucher.type).name,
quantity=inv.quantity,
price=inv.rate,
url=[
"/",
VoucherType.by_id(inv.voucher.type).name.replace(" ", "-").lower(),
str(inv.voucher.id),
],
)
for inv in batch.inventories
],
)
)
return issue
def fix_batch_prices(db: Session) -> None:
quantities = (
db.execute(
select(Batch)
.join(Batch.product)
.join(Batch.inventories)
.join(Inventory.voucher)
.where(Batch.rate != Inventory.rate)
.options(contains_eager(Batch.product), contains_eager(Batch.inventories).contains_eager(Inventory.voucher))
)
.unique()
.scalars()
.all()
)
for batch in quantities:
for inv in batch.inventories:
refresh_voucher(inv.voucher.id, batch.product_id, db)

@ -1,18 +1,10 @@
import uuid
from fastapi import APIRouter, Security
from sqlalchemy import delete, desc, distinct, func, over, select, update
from sqlalchemy import delete, desc, distinct, func, over, select
from sqlalchemy.orm import Session
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionFuture
from ..models.attendance import Attendance
from ..models.batch import Batch
from ..models.cost_centre import CostCentre
from ..models.inventory import Inventory
from ..models.journal import Journal
from ..models.voucher import Voucher
from ..models.voucher_type import VoucherType
from ..schemas.user import UserToken
@ -24,9 +16,6 @@ def post_check_db(user: UserToken = Security(get_user)):
info = {}
with SessionFuture() as db:
info["batches"] = batches(db)
info["batchDates"] = batch_dates(db)
#TODO: Also check the prices in the batches to errors
duplicate_attendances = get_duplicate_attendances(db)
if duplicate_attendances > 0:
fix_duplicate_attendances(db)
@ -65,85 +54,3 @@ def fix_duplicate_attendances(db: Session) -> None:
.subquery()
)
db.execute(delete(Attendance).where(~Attendance.id.in_(sub), Attendance.is_valid == True)) # noqa: E712
def batches(db: Session):
quantities = db.execute(
select(Inventory.batch_id, func.sum(Inventory.quantity * Journal.debit))
.join(Inventory.voucher)
.join(Voucher.journals)
.where(
Voucher.type.in_(
[
VoucherType.by_name("Purchase").id,
VoucherType.by_name("Purchase Return").id,
VoucherType.by_name("Issue").id,
VoucherType.by_name("Opening Batches").id,
]
),
Journal.cost_centre_id == CostCentre.cost_centre_purchase(),
)
.group_by(Inventory.batch_id)
).all()
issue = []
for batch_id, quantity in quantities:
batch = db.execute(select(Batch).where(Batch.id == batch_id)).scalar_one()
if batch.quantity_remaining == quantity:
continue
if quantity >= 0:
db.execute(update(Batch).where(Batch.id == batch_id).values(quantity_remaining=quantity))
else:
issue.append(
{
"product": batch.product.full_name,
"date": batch.name,
"showing": batch.quantity_remaining,
"actual": quantity,
"details": batch_details(batch_id, db),
}
)
return issue
def batch_details(batch_id: uuid.UUID, db: Session):
details = db.execute(
select(Voucher.date, Voucher.type, Inventory.quantity)
.join(Inventory.voucher)
.join(Voucher.journals)
.where(
Inventory.batch_id == batch_id,
Voucher.type.in_(
[
VoucherType.by_name("Purchase").id,
VoucherType.by_name("Purchase Return").id,
VoucherType.by_name("Issue").id,
VoucherType.by_name("Opening Batches").id,
]
),
Journal.cost_centre_id == CostCentre.cost_centre_purchase(),
)
).all()
return [{"date": x.date, "type": VoucherType.by_id(x.type).name, "quantity": x.quantity} for x in details]
def batch_dates(db: Session):
quantities = db.execute(
select(Batch, Voucher.date, Voucher.type)
.join(Batch.product)
.join(Batch.inventories)
.join(Inventory.voucher)
.where(Voucher.date < Batch.name)
).all()
issue = []
for batch, date_, type_ in quantities:
issue.append(
{
"product": batch.product.full_name,
"batchDate": batch.name,
"voucherDate": date_,
"type": VoucherType.by_id(type_).name,
}
)
return issue

@ -327,6 +327,25 @@ def update_journals(
item.amount = amount
def refresh_voucher(id_: uuid.UUID, product_id: uuid.UUID, db: Session) -> None:
try:
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one()
voucher.last_edit_date = datetime.utcnow()
inv = next(i for i in voucher.inventories if i.product_id == product_id)
batch = db.execute(select(Batch).where(Batch.id == inv.batch_id)).scalar_one()
inv.rate = batch.rate
inv.tax = batch.tax
inv.discount = batch.discount
amount = sum(i.amount for i in voucher.inventories)
for journal in voucher.journals:
journal.amount = amount
except SQLAlchemyError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e),
)
@router.get("/{id_}", response_model=output.Voucher)
def get_id(
id_: uuid.UUID,

@ -0,0 +1,50 @@
import uuid
from datetime import date, datetime
from decimal import Decimal
from typing import List
from pydantic import validator
from pydantic.main import BaseModel
from . import to_camel
class BatchIntegrityItem(BaseModel):
date_: date
type_: str
url: List[str]
quantity: Decimal
price: Decimal
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")}
@validator("date_", pre=True)
def parse_start_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()
class BatchIntegrity(BaseModel):
id_: uuid.UUID
product: str
date_: date
price: Decimal
showing: Decimal
actual: Decimal
details: List[BatchIntegrityItem]
class Config:
anystr_strip_whitespace = True
alias_generator = to_camel
json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")}
@validator("date_", pre=True)
def parse_start_date(cls, value):
if isinstance(value, date):
return value
return datetime.strptime(value, "%d-%b-%Y").date()

@ -21,6 +21,11 @@ const appRoutes: Routes = [
loadChildren: () =>
import('./balance-sheet/balance-sheet.module').then((mod) => mod.BalanceSheetModule),
},
{
path: 'batch-integrity-report',
loadChildren: () =>
import('./batch-integrity-report/batch-integrity-report.module').then((mod) => mod.BatchIntegrityReportModule),
},
{
path: 'cash-flow',
loadChildren: () => import('./cash-flow/cash-flow.module').then((mod) => mod.CashFlowModule),

@ -58,7 +58,7 @@ export class LoginComponent implements OnInit, AfterViewInit {
// .pipe(first())
.subscribe(
() => {
this.router.navigate([this.returnUrl]);
this.router.navigateByUrl(this.returnUrl);
},
(error) => {
if (error.status === 401 && error.error.detail === 'Client is not registered') {

@ -0,0 +1,16 @@
import { DataSource } from '@angular/cdk/collections';
import { Observable, of as observableOf } from 'rxjs';
import { BatchIntegrity } from './batch-integrity';
export class BatchIntegrityReportDatasource extends DataSource<BatchIntegrity> {
constructor(public data: BatchIntegrity[]) {
super();
}
connect(): Observable<BatchIntegrity[]> {
return observableOf(this.data);
}
disconnect() {}
}

@ -0,0 +1,13 @@
import { BatchIntegrityReportRoutingModule } from './batch-integrity-report-routing.module';
describe('BatchIntegrityRoutingModule', () => {
let batchIntegrityRoutingModule: BatchIntegrityReportRoutingModule;
beforeEach(() => {
batchIntegrityRoutingModule = new BatchIntegrityReportRoutingModule();
});
it('should create an instance', () => {
expect(batchIntegrityRoutingModule).toBeTruthy();
});
});

@ -0,0 +1,30 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from '../auth/auth-guard.service';
import { BatchIntegrityReportComponent } from './batch-integrity-report.component';
import { BatchIntegrityResolverService } from './batch-integrity-resolver.service';
const batchIntegrityRoutes: Routes = [
{
path: '',
component: BatchIntegrityReportComponent,
canActivate: [AuthGuard],
data: {
permission: 'Product Ledger',
},
resolve: {
info: BatchIntegrityResolverService,
},
runGuardsAndResolvers: 'always',
},
];
@NgModule({
imports: [CommonModule, RouterModule.forChild(batchIntegrityRoutes)],
exports: [RouterModule],
providers: [BatchIntegrityResolverService],
})
export class BatchIntegrityReportRoutingModule {}

@ -0,0 +1,19 @@
.right {
display: flex;
justify-content: flex-end;
}
.my-margin {
margin: 0 12px;
}
.selected {
background: #fff3cd;
}
.unposted {
background: #f8d7da;
}
.mat-column-date,
.mat-column-voucherType {
max-width: 100px;
}

@ -0,0 +1,34 @@
<mat-card>
<mat-card-title-group>
<mat-card-title>BatchReport</mat-card-title>
</mat-card-title-group>
<mat-card-content>
<mat-table [dataSource]="dataSource" aria-label="Elements">
<!-- Batch Column -->
<ng-container matColumnDef="batch">
<mat-header-cell *matHeaderCellDef>Batch</mat-header-cell>
<mat-cell *matCellDef="let row">
{{ row.product }} dated {{ row.date }} @ {{ row.price | currency: 'INR' }} is showing
{{ row.showing }} instead of {{ row.actual }}
</mat-cell>
</ng-container>
<!-- Details Column -->
<ng-container matColumnDef="details">
<mat-header-cell *matHeaderCellDef>Details</mat-header-cell>
<mat-cell *matCellDef="let row">
<ul>
<li *ngFor="let detail of row.details">
<a [routerLink]="detail.url">{{ detail.type }}</a> on {{ detail.date }} of
{{ detail.quantity }} @
{{ detail.price | currency: 'INR' }}
</li>
</ul>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
</mat-table>
</mat-card-content>
</mat-card>

@ -0,0 +1,29 @@
import { HttpClientModule } from '@angular/common/http';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { BatchIntegrityReportComponent } from './batch-integrity-report.component';
describe('BatchIntegrityComponent', () => {
let component: BatchIntegrityReportComponent;
let fixture: ComponentFixture<BatchIntegrityReportComponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule, RouterTestingModule],
declarations: [BatchIntegrityReportComponent],
}).compileComponents();
}),
);
beforeEach(() => {
fixture = TestBed.createComponent(BatchIntegrityReportComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,30 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { BatchIntegrity } from './batch-integrity';
import { BatchIntegrityReportDatasource } from './batch-integrity-report-datasource';
@Component({
selector: 'app-batch-integrity',
templateUrl: './batch-integrity-report.component.html',
styleUrls: ['./batch-integrity-report.component.css'],
})
export class BatchIntegrityReportComponent implements OnInit {
info: BatchIntegrity[] = [];
dataSource: BatchIntegrityReportDatasource = new BatchIntegrityReportDatasource(this.info);
displayedColumns = ['batch', 'details'];
constructor(private route: ActivatedRoute, private router: Router) {}
ngOnInit() {
this.route.data.subscribe((value) => {
const data = value as { info: BatchIntegrity[] };
this.info = data.info;
this.dataSource = new BatchIntegrityReportDatasource(this.info);
});
}
refresh() {
this.router.navigate([]);
}
}

@ -0,0 +1,13 @@
import { BatchIntegrityReportModule } from './batch-integrity-report.module';
describe('BatchIntegrityModule', () => {
let batchIntegrityModule: BatchIntegrityReportModule;
beforeEach(() => {
batchIntegrityModule = new BatchIntegrityReportModule();
});
it('should create an instance', () => {
expect(batchIntegrityModule).toBeTruthy();
});
});

@ -0,0 +1,73 @@
import { A11yModule } from '@angular/cdk/a11y';
import { CdkTableModule } from '@angular/cdk/table';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { ReactiveFormsModule } from '@angular/forms';
import { MomentDateAdapter } from '@angular/material-moment-adapter';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import {
DateAdapter,
MAT_DATE_FORMATS,
MAT_DATE_LOCALE,
MatNativeDateModule,
} from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatRadioModule } from '@angular/material/radio';
import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { SharedModule } from '../shared/shared.module';
import { BatchIntegrityReportRoutingModule } from './batch-integrity-report-routing.module';
import { BatchIntegrityReportComponent } from './batch-integrity-report.component';
export const MY_FORMATS = {
parse: {
dateInput: 'DD-MMM-YYYY',
},
display: {
dateInput: 'DD-MMM-YYYY',
monthYearLabel: 'MMM YYYY',
dateA11yLabel: 'DD-MMM-YYYY',
monthYearA11yLabel: 'MMM YYYY',
},
};
@NgModule({
imports: [
A11yModule,
CommonModule,
CdkTableModule,
MatButtonModule,
MatCardModule,
MatCheckboxModule,
MatIconModule,
MatInputModule,
MatNativeDateModule,
MatPaginatorModule,
MatProgressSpinnerModule,
MatRadioModule,
MatSortModule,
MatTableModule,
SharedModule,
BatchIntegrityReportRoutingModule,
MatExpansionModule,
MatDatepickerModule,
ReactiveFormsModule,
FlexLayoutModule,
],
declarations: [BatchIntegrityReportComponent],
providers: [
{ provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] },
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS },
],
})
export class BatchIntegrityReportModule {}

@ -0,0 +1,20 @@
import { HttpClientModule } from '@angular/common/http';
import { inject, TestBed } from '@angular/core/testing';
import { BatchIntegrityReportService } from './batch-integrity-report.service';
describe('BatchIntegrityService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule],
providers: [BatchIntegrityReportService],
});
});
it('should be created', inject(
[BatchIntegrityReportService],
(service: BatchIntegrityReportService) => {
expect(service).toBeTruthy();
},
));
});

@ -0,0 +1,27 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/internal/Observable';
import { catchError } from 'rxjs/operators';
import { ErrorLoggerService } from '../core/error-logger.service';
import { BatchIntegrity } from './batch-integrity';
const url = '/api/batchIntegrity';
const serviceName = 'BatchIntegrityReportService';
@Injectable({
providedIn: 'root',
})
export class BatchIntegrityReportService {
constructor(private http: HttpClient, private log: ErrorLoggerService) {}
batchIntegrity(): Observable<BatchIntegrity[]> {
const url = '/api/batch-integrity';
return this.http
.post<BatchIntegrity[]>(url, {})
.pipe(catchError(this.log.handleError(serviceName, 'batchIntegrity'))) as Observable<
BatchIntegrity[]
>;
}
}

@ -0,0 +1,20 @@
import { HttpClientModule } from '@angular/common/http';
import { inject, TestBed } from '@angular/core/testing';
import { BatchIntegrityResolverService } from './batch-integrity-resolver.service';
describe('BatchIntegrityResolverService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule],
providers: [BatchIntegrityResolverService],
});
});
it('should be created', inject(
[BatchIntegrityResolverService],
(service: BatchIntegrityResolverService) => {
expect(service).toBeTruthy();
},
));
});

@ -0,0 +1,17 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
import { Observable } from 'rxjs/internal/Observable';
import { BatchIntegrity } from './batch-integrity';
import { BatchIntegrityReportService } from './batch-integrity-report.service';
@Injectable({
providedIn: 'root',
})
export class BatchIntegrityResolverService implements Resolve<BatchIntegrity[]> {
constructor(private ser: BatchIntegrityReportService) {}
resolve(route: ActivatedRouteSnapshot): Observable<BatchIntegrity[]> {
return this.ser.batchIntegrity();
}
}

@ -1,14 +1,18 @@
export class BatchDate {
export class BatchIntegrity {
product: string;
batchDate: string;
voucherDate: string;
type: string;
date: string;
price: number;
showing: number;
actual: number;
details: BatchDetail[];
public constructor(init?: Partial<BatchDate>) {
public constructor(init?: Partial<BatchIntegrity>) {
this.product = '';
this.batchDate = '';
this.voucherDate = '';
this.type = '';
this.date = '';
this.price = 0;
this.showing = 0;
this.actual = 0;
this.details = [];
Object.assign(this, init);
}
}
@ -16,29 +20,16 @@ export class BatchDate {
export class BatchDetail {
date: string;
type: string;
url: string[];
quantity: number;
price: number;
public constructor(init?: Partial<BatchDetail>) {
this.date = '';
this.type = '';
this.url = [];
this.quantity = 0;
Object.assign(this, init);
}
}
export class IntegrityBatch {
product: string;
date: string;
showing: number;
actual: number;
details: BatchDetail[];
public constructor(init?: Partial<IntegrityBatch>) {
this.product = '';
this.date = '';
this.showing = 0;
this.actual = 0;
this.details = [];
this.price = 0;
Object.assign(this, init);
}
}

@ -33,6 +33,7 @@
<a mat-menu-item routerLink="/closing-stock">Closing Stock</a>
<a mat-menu-item routerLink="/stock-movement">Stock Movement</a>
<a mat-menu-item routerLink="/rate-contracts">Rate Contracts</a>
<a mat-menu-item routerLink="/batch-integrity-report">Batch Integrity</a>
</mat-menu>
<button mat-button [matMenuTriggerFor]="productReportMenu">Product Reports</button>

@ -300,56 +300,6 @@
</mat-card-content>
</mat-card>
</mat-tab>
<mat-tab label="Integrity Check">
<mat-card>
<mat-card-content>
<div
fxLayout="row"
fxLayoutAlign="space-around start"
fxLayout.lt-md="column"
fxLayoutGap="20px"
fxLayoutGap.lt-md="0px"
>
<button mat-raised-button color="warn" (click)="checkIntegrity()" fxFlex="100">
Integrity Check
</button>
</div>
<div
fxLayout="row"
fxLayoutAlign="space-around start"
fxLayout.lt-md="column"
fxLayoutGap="20px"
fxLayoutGap.lt-md="0px"
>
<ul>
<li *ngFor="let batch of batches">
Batch of {{ batch.product }} dated {{ batch.date }} is showing
{{ batch.showing }} instead of {{ batch.actual }}
<ul>
<li *ngFor="let detail of batch.details">
{{ detail.type }} of {{ detail.quantity }} on {{ detail.date }}
</li>
</ul>
</li>
</ul>
</div>
<div
fxLayout="row"
fxLayoutAlign="space-around start"
fxLayout.lt-md="column"
fxLayoutGap="20px"
fxLayoutGap.lt-md="0px"
>
<ul>
<li *ngFor="let batch of batchDates">
{{ batch.type }} of {{ batch.product }} dated {{ batch.batchDate }} on
{{ batch.voucherDate }}
</li>
</ul>
</div>
</mat-card-content>
</mat-card>
</mat-tab>
<mat-tab label="Maintenance Mode">
<mat-card>
<mat-card-content>

@ -15,7 +15,6 @@ import { ToasterService } from '../core/toaster.service';
import { ProductService } from '../product/product.service';
import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component';
import { BatchDate, IntegrityBatch } from './integrity-batch';
import { LockDataSource } from './lock-datasource';
import { LockInfo } from './lock-info';
import { SettingsService } from './settings.service';
@ -45,8 +44,6 @@ export class SettingsComponent implements OnInit {
product: Product = new Product();
products: Observable<Product[]>;
batches: IntegrityBatch[] = [];
batchDates: BatchDate[] = [];
maintenance: { enabled: boolean; user: string };
displayedColumns = ['index', 'validity', 'voucherTypes', 'accountTypes', 'lock', 'action'];
@ -291,19 +288,6 @@ export class SettingsComponent implements OnInit {
);
}
checkIntegrity() {
this.ser.checkDatabaseIntegrity().subscribe(
(x) => {
this.batches = x.batches;
this.batchDates = x.batchDates;
this.toaster.show('Success', 'Database checked, it is fine!');
},
(error) => {
this.toaster.show('Danger', error);
},
);
}
toggleMaintenance() {
this.ser.setMaintenance(!this.maintenance.enabled).subscribe((x) => {
this.maintenance = x;

@ -6,7 +6,6 @@ import { catchError } from 'rxjs/operators';
import { ErrorLoggerService } from '../core/error-logger.service';
import { Product } from '../core/product';
import { BatchDate, IntegrityBatch } from './integrity-batch';
import { LockInfo } from './lock-info';
const serviceName = 'SettingsService';
@ -83,21 +82,12 @@ export class SettingsService {
}>;
}
checkDatabaseIntegrity(): Observable<{
attendanceCount: number;
batches: IntegrityBatch[];
batchDates: BatchDate[];
}> {
checkDatabaseIntegrity(): Observable<{ attendanceCount: number }> {
const url = '/api/db-integrity';
return this.http
.post<{ attendanceCount: number; batches: IntegrityBatch[]; batchDates: BatchDate[] }>(
url,
{},
)
.post<{ attendanceCount: number }>(url, {})
.pipe(catchError(this.log.handleError(serviceName, 'checkDatabaseIntegrity'))) as Observable<{
attendanceCount: number;
batches: IntegrityBatch[];
batchDates: BatchDate[];
}>;
}
}