import csv import os from itertools import groupby from typing import Dict, List import click class MARGReordering: def __init__( self, sale_stock_filename: str, products_filename: str, fordays: int, number_of_days: int, less_than_x_days: int ) -> None: self.sale_stock_file = sale_stock_filename self.products_filename = products_filename self.fordays = fordays self.number_of_days = number_of_days self.less_than_x_days = less_than_x_days @property def products(self) -> Dict[str, Dict]: products = {} with open(self.products_filename) as csv_file: csv_reader = csv.DictReader(csv_file) for row in csv_reader: key = row['Product Name'] if key: products.update({key: row}) return products def get_items_to_reoder(self) -> List: with open(self.sale_stock_file) as csv_file: csv_reader = csv.DictReader(csv_file) items_to_reoder = [] for row in csv_reader: if all([ 0 <= int(row['fordays']) <= self.fordays, int(row['sale']), ]): reorder_required_count = int(row['sale']) - int(row['stock']) item_box_size = self.products.get(row['item'], {}).get('Manufacturer', 0) item_box_size = int(item_box_size) if item_box_size else 10 reorder_box_count = 0 if item_box_size: reorder_box_count = round(reorder_required_count / item_box_size) if not reorder_box_count: reorder_box_count = 1 reorder_count = reorder_box_count * item_box_size items_to_reoder.append({ 'code': row['code'], 'company': row['company'], 'item': row['item'], 'zero_stock': int(row['fordays']) == 0, 'less_than_x_days': int(row['fordays']) < self.less_than_x_days, 'reorder_required_count': reorder_required_count, 'reorder_count': reorder_count, 'box_size': item_box_size, 'reorder_box_count': reorder_box_count, 'reorder_value': round(float(row['cost']) * reorder_count, 2) }) return items_to_reoder def process_data(self) -> None: items_to_reorder = self.get_items_to_reoder() items_to_reorder.sort(key=lambda x: x['company']) for company_name, items in groupby(items_to_reorder, key=lambda x: x['company']): items = list(items) total_row = self.calculate_total(items) items.insert(len(items), total_row) self.export_to_csv(company_name, items) def calculate_total(self, items_to_reorder): zero_stock = len([ item['zero_stock'] for item in items_to_reorder if item['zero_stock'] ]) less_than_x_days = len([ item['less_than_x_days'] for item in items_to_reorder if item['less_than_x_days'] ]) total_count = len(items_to_reorder) return { 'code': 'TOTAL', 'company': '', 'item': '', 'zero_stock': f'{zero_stock}/{total_count}', 'less_than_x_days': f'{less_than_x_days}/{total_count}', 'reorder_count': '', 'reorder_value': round(sum([item['reorder_value'] for item in items_to_reorder]), 2) } def export_to_csv(self, comapany_name: str, items: List[Dict]) -> None: header = items[0].keys() if not os.path.exists('output'): os.makedirs('output') with open(f'output/{comapany_name}.csv', 'w', newline='') as output_file: dict_writer = csv.DictWriter(output_file, header) dict_writer.writeheader() dict_writer.writerows(items) @click.command() @click.option('--sale-stock-data', type=click.STRING, help='CSV exported from MARG containg sale/stock data') @click.option('--products-data', type=click.STRING, help='CSV exported from MARG containg products data') @click.option('--fordays', type=click.INT, help='Threshold in number of days to calculate reordering') @click.option('--number-of-days', type=click.INT, help='No. of days data in sale/stock file') @click.option('--less-than-x-days', type=click.INT) def main( sale_stock_data: str, products_data: str, fordays: int, number_of_days: int, less_than_x_days: int ): MARGReordering( sale_stock_data, products_data, fordays, number_of_days, less_than_x_days ).process_data() if __name__ == "__main__": main()