AX系统里,X++语言按月份+产品型号统计产量是常规操作,如果要把某个日期范围里生产过的产品型号做为栏(相当于字段)来统计生产数量,需要一些折中的代码来实现。
下面代码是实例,目的:输入从日期 和 到期期 两个参数,统计生产数量,要求每个月轮询,产品型号作为列头,结果输出到Excel里。
void ProdSum2Excel(TransDate _fromDate, TransDate _toDate)
{
// Excel 對象
SysExcelApplication _excel;
SysExcelWorkbooks _books;
SysExcelWorkbook _book;
SysExcelWorksheets _sheets;
SysExcelWorksheet _sheet;
SysExcelCells _cells;
// 統計用 Map: Key = "26年5月|SP|風筒", Value = 數量
Map _dataMap;
str _mapKey;
int _mapValue;
// 列表(保持輸出順序)
List _monthList;
ListEnumerator _monthEnum;
List _prodTypeList;
ListEnumerator _typeEnum;
List _companyList;
ListEnumerator _compEnum;
// 當前處理變量
str _currentMonth;
str _currentCompany;
str _currentType;
int _cellValue;
int _rowTotal;
int _colTotal;
int _row;
int _col;
int _startRow;
int _i;
// 數據源變量
SPL_DailyProd _dailyProd;
SPL_OrderList _orderList;
InventTable _inventTable;
ProdLine _prodLine;
TransDate _prodDate;
str _company;
str _prodType;
int _qty;
str _monthKey;
// 生成月份列表用變量
TransDate _tmpDate;
int _tmpYear;
int _tmpMonth;
str _tmpMonthKey;
int _loopCount; // 防止死循環
str _tmpStr;
;
// 1. 初始化分公司列表(固定順序)
_companyList = new List(Types::String);
_companyList.addEnd("SP");
_companyList.addEnd("JM");
_companyList.addEnd("SR");
//_companyList.addEnd("JA"); //不生產成品
// 2. 根據 _fromDate 和 _toDate 生成月份列表
_monthList = new List(Types::String);
if (_fromDate > _toDate)
{
info("開始日期不能大於結束日期");
return;
}
_tmpDate = _fromDate;
_loopCount = 0;
while (_tmpDate <= _toDate)
{
_loopCount++;
if (_loopCount > 120)
{
info("月份循環超過安全限制,請檢查日期範圍");
break;
}
_tmpYear = year(_tmpDate);
_tmpMonth = MthOfYr(_tmpDate);
_tmpMonthKey = strFmt("%1年%2月",
num2str(_tmpYear - 2000, 0, 0, 0, 0),
num2str(_tmpMonth, 0, 0, 0, 0));
if (!element.isInList(_monthList, _tmpMonthKey))
{
_monthList.addEnd(_tmpMonthKey);
}
// 移到下個月1號(使用 dateMthFwd 更安全)
_tmpDate = dateMthFwd(_tmpDate, 1);
// 確保是下個月1號
_tmpDate = mkDate(1, MthOfYr(_tmpDate), year(_tmpDate));
}
// 3. 初始化數據結構
_dataMap = new Map(Types::String, Types::Integer);
_prodTypeList = new List(Types::String);
// 4. 從數據源讀取並統計數據(同時提取產品類型)
while select SPL_ProdDate, SPL_DailyQty from _dailyProd
where _dailyProd.SPL_ProdDate >= _fromDate
&& _dailyProd.SPL_ProdDate <= _toDate
join ItemId from _orderList
where _orderList.SalesLineRecId == _dailyProd.SalesLineRecId
join SPL_NameAlias from _inventTable
where _inventTable.ItemId == _orderList.ItemId
join SPL_Factory from _prodLine
where _prodLine.ProdLineId == _dailyProd.ProdLineId
{
_prodDate = _dailyProd.SPL_ProdDate;
_company = _prodLine.SPL_Factory; // 分公司
_prodType = _inventTable.SPL_NameAlias; // 簡稱/產品類型
_qty = _dailyProd.SPL_DailyQty; // 完成數量
// 生成月份Key: "26年5月"
_monthKey = strFmt("%1年%2月",
num2str(year(_prodDate) - 2000, 0, 0, 0, 0),
num2str(MthOfYr(_prodDate), 0, 0, 0, 0));
// 動態收集產品類型(只收集過濾後數據中出現的)
if (_prodType != "" && !element.isInList(_prodTypeList, _prodType))
{
_prodTypeList.addEnd(_prodType);
}
// Map Key: "26年5月|SP|風筒"
_mapKey = strFmt("%1|%2|%3", _monthKey, _company, _prodType);
if (_dataMap.exists(_mapKey))
{
_mapValue = _dataMap.lookup(_mapKey) + _qty;
_dataMap.remove(_mapKey);
_dataMap.insert(_mapKey, _mapValue);
}
else
{
_dataMap.insert(_mapKey, _qty);
}
}
// 5. 創建 Excel
_excel = SysExcelApplication::construct();
_excel.visible(false);
_books = _excel.workbooks();
_book = _books.add();
_sheets = _book.worksheets();
_sheet = _sheets.itemFromNum(1);
_tmpStr= strFmt("%1至%2生產統計",date2str(_fromDate,321,2,3,2,3,2),date2str(_toDate,321,2,3,2,3,2));
_sheet.name(_tmpStr);
_cells = _sheet.cells();
_row = 1;
// 6. 按月份循環輸出
_monthEnum = _monthList.getEnumerator();
while (_monthEnum.moveNext())
{
_currentMonth = _monthEnum.current();
// ---- 輸出月份標題 ----
_cells.item(_row, 1).value(_currentMonth);
_row++;
// ---- 輸出表頭 ----
_col = 1;
_cells.item(_row, _col).value("分公司");
_col++;
_typeEnum = _prodTypeList.getEnumerator();
while (_typeEnum.moveNext())
{
_cells.item(_row, _col).value(_typeEnum.current());
_col++;
}
_cells.item(_row, _col).value("小計");
_row++;
_startRow = _row;
// ---- 輸出各分公司數據 ----
_compEnum = _companyList.getEnumerator();
while (_compEnum.moveNext())
{
_currentCompany = _compEnum.current();
_col = 1;
_cells.item(_row, _col).value(_currentCompany);
_col++;
_rowTotal = 0;
_typeEnum = _prodTypeList.getEnumerator();
while (_typeEnum.moveNext())
{
_currentType = _typeEnum.current();
_mapKey = strFmt("%1|%2|%3", _currentMonth, _currentCompany, _currentType);
if (_dataMap.exists(_mapKey))
{
_cellValue = _dataMap.lookup(_mapKey);
_cells.item(_row, _col).value(_cellValue);
_rowTotal += _cellValue;
}
else
{
_cells.item(_row, _col).value(0);
}
_col++;
}
// 小計
_cells.item(_row, _col).value(_rowTotal);
_row++;
}
// ---- 輸出累計行 ----
_col = 1;
_cells.item(_row, _col).value("累計");
_col++;
_typeEnum = _prodTypeList.getEnumerator();
while (_typeEnum.moveNext())
{
_currentType = _typeEnum.current();
_colTotal = 0;
// 遍歷各分公司,累加該產品類型的值
_compEnum = _companyList.getEnumerator();
while (_compEnum.moveNext())
{
_currentCompany = _compEnum.current();
_mapKey = strFmt("%1|%2|%3", _currentMonth, _currentCompany, _currentType);
if (_dataMap.exists(_mapKey))
{
_colTotal += _dataMap.lookup(_mapKey);
}
}
_cells.item(_row, _col).value(_colTotal);
_col++;
}
// 累計小計 = 各分公司小計之和
_colTotal = 0;
_compEnum = _companyList.getEnumerator();
while (_compEnum.moveNext())
{
_currentCompany = _compEnum.current();
_rowTotal = 0;
_typeEnum = _prodTypeList.getEnumerator();
while (_typeEnum.moveNext())
{
_currentType = _typeEnum.current();
_mapKey = strFmt("%1|%2|%3", _currentMonth, _currentCompany, _currentType);
if (_dataMap.exists(_mapKey))
{
_rowTotal += _dataMap.lookup(_mapKey);
}
}
_colTotal += _rowTotal;
}
_cells.item(_row, _col).value(_colTotal);
_row++;
_row++; // 空一行分隔
}
// 7. 自動調整列寬
_col = 2 + _prodTypeList.elements();
for (_i = 1; _i <= _col; _i++)
{
//_sheet.columns().item(_i).autoFit();
_sheet.columns().item(_i).comObject().columnWidth(8.8);
_sheet.columns().item(_i).numberFormat("#,##0");
_sheet.columns().item(_i).comObject().horizontalAlignment(-4108);
}
// 8. 直接顯示 Excel,讓用戶手工保存
_excel.visible(true);
info("Excel 報表已生成,請切換到Excel程式");
}
上述代码调用了本FORM中中外两个Method,如下:
//列號轉字母(1->A, 2->B...)
str colNumToLetter(int _col)
{
str _result = "";
int _dividend = _col;
int _modulo;
;
while (_dividend > 0)
{
_modulo = (_dividend - 1) mod 26;
_result = num2char(65 + _modulo) + _result;
_dividend = (_dividend - 1) / 26;
}
return _result;
}
//檢查字符串是否在List中
boolean isInList(List _list, str _value)
{
ListEnumerator _enum;
;
_enum = _list.getEnumerator();
while (_enum.moveNext())
{
if (_enum.current() == _value)
return true;
}
return false;
}
