private Division Read_PartsDivision(ExcelWorksheet sheet, Regex div_name, Regex div_total, string expecting_div_name)
        {
            int row = CurrentWorkingRowIndex;
            int col = EXCEL_COL_START_INDEX;

            string cell = readcell_safe(sheet, row, col);
            Match  m    = div_name.Match(cell);

            if (!m.Success)
            {
                ExcelParseException ex = new ExcelParseException();
                ex.ExcelRow = row;
                ex.ExcelCol = col;
                ex.Hint     = $"\"{expecting_div_name}\" 문자열을 찾을 수 없습니다.";
                throw new ApplicationException(ex.ToString(), ex);
            }

            Division div = new Division();

            div.name_in_excel  = cell;
            div.name_justified = m.Groups[m.Groups.Count - 1].Value;

            CurrentWorkingRowIndex = row;
            Read_Parts_Units(sheet, div, div_total);

            return(div);
        }
        private Unit Read_Cost_Unit(ExcelWorksheet sheet, int start_row, Regex div_total, PartType partType)
        {
            int    row  = start_row;
            int    col  = UNIT_NAME_COLSPAN_START_COLUMN_INDEX;
            string cell = readcell_safe(sheet, row, col);
            Match  m    = regUnitName.Match(cell);

            if (!m.Success)
            {
                ExcelParseException ex = new ExcelParseException();
                ex.ExcelRow = row;
                ex.ExcelCol = col;
                ex.Hint     = "유닛 이름은 (1) 처럼 괄호 안의 숫자로 시작해야 하는데, 그러한 문자열을 찾을 수 없습니다.";
                throw new ApplicationException(ex.ToString(), ex);
            }

            Unit unit = new Unit();

            unit.name_in_excel     = cell;
            unit.name_justified    = m.Groups[m.Groups.Count - 1].Value;
            unit.vendor_totalprice = readcell_decimal_safe(sheet, row, (int)ExcelColumns.금액);

            row++;
            PartGroup pg = new PartGroup();

            while (row <= MaxRowCount)
            {
                cell = readcell_safe(sheet, row, col);
                m    = div_total.Match(cell);
                if (m.Success)
                {
                    // division ended
                    break;
                }

                m = regUnitName.Match(cell);
                if (m.Success)
                {
                    // new unit started
                    break;
                }

                ReviewPartInfo rpi = ReadVendorPart(sheet, row);
                rpi.parttype = partType;
                pg.ReviewPartInfos.Add(rpi);
                row++;
            }
            unit.partGroups.Add(pg);

            //
            CurrentWorkingRowIndex = row;
            return(unit);
        }
        private Unit Read_Part_Unit(ExcelWorksheet sheet, int start_row)
        {
            int    row  = start_row;
            int    col  = UNIT_NAME_COLSPAN_START_COLUMN_INDEX;
            string cell = readcell_safe(sheet, row, col);
            Match  m    = regUnitName.Match(cell);

            if (!m.Success)
            {
                ExcelParseException ex = new ExcelParseException();
                ex.ExcelRow = row;
                ex.ExcelCol = col;
                ex.Hint     = "유닛 이름은 (1) 처럼 괄호 안의 숫자로 시작해야 하는데, 그러한 문자열을 찾을 수 없습니다.";
                throw new ApplicationException(ex.ToString(), ex);
            }

            Unit unit = new Unit();

            unit.name_in_excel  = cell;
            unit.name_justified = m.Groups[m.Groups.Count - 1].Value;

            //
            unit.vendor_count      = readcell_decimal_safe(sheet, row, (int)ExcelColumns.수량);
            unit.vendor_unitprice  = readcell_decimal_safe(sheet, row, (int)ExcelColumns.단가);
            unit.vendor_totalprice = readcell_decimal_safe(sheet, row, (int)ExcelColumns.금액);

            // read 구매품
            row = ReadPartGroup(sheet, row + 1, unit);

            // read 가공품
            row = ReadPartGroup(sheet, row + 1, unit);

            //
            CurrentWorkingRowIndex = row + 1;

            return(unit);
        }