Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | 2x 2x 2x 2x 2x 2x 2x 22x 2x 2x 1x 1x 4x 4x 4x 1x 4x 1x 4x 4x 11x 11x 7x 4x 2x 2x 1x 1x 2x 1x 1x 5x 4x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 121x 1x 1x 1x | import {
Injectable,
NotFoundException,
BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Barang } from '../entities/barang.entity';
import { Repository, FindOptionsWhere, ILike, LessThanOrEqual } from 'typeorm';
import { CreateBarangDto } from './dto/create-barang.dto';
import { UpdateBarangDto } from './dto/update-barang.dto';
import { AddStokDto } from './dto/add-stok.dto';
import * as PdfPrinter from 'pdfmake';
import * as path from 'path';
@Injectable()
export class BarangService {
constructor(
@InjectRepository(Barang)
private barangRepo: Repository<Barang>,
) {}
async create(dto: CreateBarangDto): Promise<Barang> {
const exist = await this.barangRepo.findOne({
where: { kode_barang: dto.kode_barang },
});
if (exist) throw new BadRequestException('Kode barang sudah terdaftar');
const barang = this.barangRepo.create({ ...dto, status_aktif: true });
return this.barangRepo.save(barang);
}
async findAll(query?: {
q?: string;
status_aktif?: boolean;
stok_kritis?: boolean;
}): Promise<Barang[]> {
const repo = this.barangRepo;
const qb = repo.createQueryBuilder('barang');
// Pencarian nama/kode
if (query?.q) {
qb.andWhere(
'(barang.nama_barang ILIKE :q OR barang.kode_barang ILIKE :q)',
{ q: `%${query.q}%` },
);
}
// Filter status aktif
if (typeof query?.status_aktif === 'boolean') {
qb.andWhere('barang.status_aktif = :status', {
status: query.status_aktif,
});
}
// Filter stok kritis
Iif (query?.stok_kritis) {
qb.andWhere('barang.stok <= barang.ambang_batas_kritis');
}
return qb.getMany();
}
async findOne(id: number): Promise<Barang> {
const barang = await this.barangRepo.findOne({ where: { id } });
if (!barang) throw new NotFoundException('Barang tidak ditemukan');
return barang;
}
async update(id: number, dto: UpdateBarangDto): Promise<Barang> {
if (dto.stok !== undefined && dto.stok < 0) {
throw new BadRequestException('Stok tidak boleh negatif');
}
const barang = await this.findOne(id);
Object.assign(barang, dto);
return this.barangRepo.save(barang);
}
async softDelete(id: number): Promise<Barang> {
const barang = await this.findOne(id);
barang.status_aktif = false;
return this.barangRepo.save(barang);
}
async remove(id: number): Promise<void> {
const barang = await this.findOne(id);
await this.barangRepo.remove(barang);
}
async addStok(id: number, dto: AddStokDto): Promise<Barang> {
const barang = await this.findOne(id);
if (!barang.status_aktif)
throw new BadRequestException('Barang tidak aktif');
barang.stok = (barang.stok ?? 0) + dto.jumlah;
return this.barangRepo.save(barang);
}
async getStokKritis(): Promise<Barang[]> {
return this.barangRepo
.createQueryBuilder('barang')
.where('barang.stok <= barang.ambang_batas_kritis')
.andWhere('barang.status_aktif = :aktif', { aktif: true })
.getMany();
}
async getBarangKritis() {
return this.barangRepo
.createQueryBuilder('barang')
.where('barang.stok <= barang.ambang_batas_kritis')
.andWhere('barang.status_aktif = :aktif', { aktif: true })
.orderBy('barang.stok', 'ASC')
.getMany();
}
async generateLaporanPenggunaanPDF(
start: string,
end: string,
): Promise<Buffer> {
// Query data penggunaan barang dari tabel detail_permintaan + permintaan
const penggunaan = await this.barangRepo.query(
`
SELECT b.nama_barang, b.satuan, SUM(d.jumlah_disetujui) as total_digunakan
FROM detail_permintaan d
JOIN barang b ON d.id_barang = b.id
JOIN permintaan p ON d.id_permintaan = p.id
WHERE p.status IN ('Disetujui', 'Disetujui Sebagian')
AND p.tanggal_permintaan BETWEEN $1 AND $2
GROUP BY b.nama_barang, b.satuan
ORDER BY b.nama_barang
`,
[start, end],
);
const fonts = {
Roboto: {
normal: path.join(__dirname, '../assets/fonts/Roboto-Regular.ttf'),
bold: path.join(__dirname, '../assets/fonts/Roboto-Bold.ttf'),
italics: path.join(__dirname, '../assets/fonts/Roboto-Italic.ttf'),
bolditalics: path.join(
__dirname,
'../assets/fonts/Roboto-BoldItalic.ttf',
),
},
};
const printer = new PdfPrinter(fonts);
const bodyRows =
penggunaan.length > 0
? penggunaan.map((row) => [
row.nama_barang,
row.total_digunakan,
row.satuan,
])
: [
[
{ text: 'Tidak ada data', colSpan: 3, alignment: 'center' },
{},
{},
],
];
const docDefinition = {
content: [
{ text: 'Laporan Penggunaan Barang', style: 'header' },
{ text: `Periode: ${start} s/d ${end}`, margin: [0, 10, 0, 10] },
{
table: {
headerRows: 1,
widths: ['*', 60, 60],
body: [
[
{ text: 'Nama Barang', style: 'tableHeader' },
{ text: 'Total Digunakan', style: 'tableHeader' },
{ text: 'Satuan', style: 'tableHeader' },
],
...bodyRows,
],
},
layout: 'lightHorizontalLines',
},
],
styles: {
header: { fontSize: 16, bold: true, alignment: 'center' },
tableHeader: { bold: true, fillColor: '#eeeeee' },
},
};
const pdfDoc = printer.createPdfKitDocument(docDefinition);
const chunks: Buffer[] = [];
return new Promise<Buffer>((resolve, reject) => {
pdfDoc.on('data', (chunk) => chunks.push(chunk));
pdfDoc.on('end', () => resolve(Buffer.concat(chunks)));
pdfDoc.on('error', reject);
pdfDoc.end();
});
}
}
|