📜 ⬆️ ⬇️

Set of 1S admin screwdrivers

image alt text


For several years, at first, forced, and then quite entertaining administration of 1C, I have accumulated a set of solutions for most of the product features. I propose to put aside high matter about clusters and SQL tuning, and shake up stocks of scripts and mechanisms that make life easier with 1C.


There will be both simple tools for creating new users and monitoring whether everyone is out of the database, as well as more sophisticated interfaces for checking the integrity of the database and its movement.


Clean Cache and Good Health


Like most complex applications, 1C after some time work get out strange errors, and sometimes there is an inexplicable behavior. Special 1C people advise in such cases to clean the cache.


If you run 1C with the / ClearCache parameter, only client-server requests will be cleared. Local metadata will remain and must be removed separately at the file and folder level. This data is stored in a user profile, in folders with long names from GUID databases. If there are not many databases on the server, such a cache can be easily removed by hand. But if the database is in the tens, then manually cleaning you will not be happy.


image alt text


In such situations, the Powershell script will help out, which is started each time the user logs off:


Get-ChildItem "$env:USERPROFILE\AppData\Local\1C\1Cv8\*","$env:USERPROFILE\AppData\Roaming\1C\1Cv8\*" | Where {$_.Name -as [guid]} |Remove-Item -Force -Recurse 

And no problems with the old cache.


Fix bugs


To fix the corrupted file database, the 1C utility includes the chdbfl.exe utility, which simply reads the contents of the database into a temporary file. If any data can not be considered - skips. However, it does not have start-up keys for automation, and the test has to be started manually.


In general, it is more correct to start checking the DB with the configurator, but this process takes much longer. If you only use the physical integrity check using chdbfl.exe , then do not forget to make a backup copy due to possible data loss.


For bases 8.1, Andrei Sklyarov created a good Check1CD tool, with two launch parameters: “fix found errors” and “path to base”.


But Check1CD lacks two things:



Since there is a Wishlist and some free time, then why not try to solve the problem yourself?


So there was a mega-script on AutoIt
 #NoTrayIcon #include <GUIConstantsEx.au3> #include <File.au3> #include <Date.au3> #include <Inet.au3> Global $oMyError = ObjEvent("AutoIt.Error","MyErrFunc") Global $Basepath="D:\Base\” ;    ,   ;,    1. If ProcessExists("1cv8.exe") or ProcessExists("chdbfl.exe") Then Msgbox(0,"!", "1c  ,   ") Exit EndIf ;,     ,     if FileExists ($Basepath&"_$NEW$_.1CD") Then MSGbox(0,"!", "   !    ") Exit EndIf ;  ,       If not FileExists ($Basepath&"1Cv8.1CD") Then Msgbox(0,"!", "   !") Exit EndIf ;,  $hgui = GUICreate(" " , 200, 200) GUICtrlCreateLabel("    ",2,2) GUICtrlCreateLabel("    30 ",2,22) GUICtrlCreateLabel(" ? ",2,42) $yes = GUICtrlCreateButton("", 5, 150, 70, 25) $no= GUICtrlCreateButton("", 75, 150, 70, 25) GUISetState(@SW_SHOW) While 1 $msg = GUIGetMsg() Select Case $msg = $GUI_EVENT_CLOSE GUIDelete() Exit Case $msg = $yes GUIDelete() _check () ExitLoop Case $msg = $no GUIDelete() Exit EndSelect WEnd ;   func _check () $sTitle="    " ProgressOn(" ", "   ", "0%",-1,-1,18) run ("C:\Program Files\1cv81\bin\chdbfl.exe","", @SW_HIDE,7) WinWait($sTitle) local $size = filegetsize($Basepath&"1Cv8.1CD") ;! ControlSend($sTitle,"", "V8FormElement9",$Basepath&"1Cv8.1CD") ControlClick($sTitle,"", "V8FormElement8") ControlClick($sTitle,"", "V8FormElement7","left",2) $filename = $Basepath&"\1Cv8.1CD" local $size1 =0 ;        While 1 $size1 = filegetsize($Basepath&"_$NEW$_.1CD") $perc=round(($size1/$size)*100) ProgressSet($perc, ", .  "& $perc & "%") sleep (1500) ;,         ;    If not FileInUse($filename) and not FileExists ($Basepath&"\_$NEW$_.1CD") Then ExitLoop EndIf WEnd sleep(5000) ; ,         ; - ! WinSetState ( $sTitle, "", @SW_SHOW ) WinSetOnTop($sTitle, "", 1) WinActivate($sTitle, "") WinWaitActive($sTitle) ControlClick($sTitle,"", "V8FormElement4") sleep(50) ControlSend($sTitle,"", "V8FormElement4","^{home}") sleep(50) ControlSend($sTitle,"", "V8FormElement4","+^{end}") sleep(50) ControlSend($sTitle,"", "V8FormElement4","^{ins}") sleep(50) $text = ClipGet() ControlClick($sTitle,"", "V8FormElement6","left",2) ProgressSet(100, "100%", " ") sleep (500) ProgressOff() ; -,     ,   . ;  e-mail ;   Outlook Express.     $sender = RegRead("HKEY_CURRENT_USER\Software\Microsoft\ _ Internet Account Manager\Accounts\00000001","SMTP Email Address") $sendername = RegRead("HKEY_CURRENT_USER\Software\Microsoft\ _ Internet Account Manager\Accounts\00000001","SMTP Display Name") If not FileExists($Basepath&"check.Log") then _FileCreate($Basepath&"\check.Log") FileWrite ($Basepath&"check.Log", _NowCalc()&@CRLF ) If $text <> "" Then FileWrite ( $Basepath&"check.Log", " :"&@CRLF) FileWrite ( $Basepath&"check.Log", $text&@CRLF) FileWrite ( $Basepath&"check.Log", " .."&@CRLF) ;   smtp. -,   If Ping("smtp-server") <> 0 then if $sender = "" or $sendername="" Then MSGbox(0,"!", "  ,    !") Else email($sender, $sendername,$text) EndIf endif ;   ,      Else FileWrite ( $Basepath&"check.Log", " "&@CRLF) EndIf $hgui = GUICreate(" " , 200, 200) GUICtrlCreateLabel("  ",2,2) $ok = GUICtrlCreateButton("", 5, 150, 70, 25) GUISetState(@SW_SHOW) While 1 $msg = GUIGetMsg() Select Case $msg = $GUI_EVENT_CLOSE or $msg=$1 GUIDelete() Exit EndSelect WEnd EndFunc Func FileInUse($filename) $handle = FileOpen($filename, 1) $result = False if $handle = -1 then $result = True FileClose($handle) return $result EndFunc func email($sender, $sendername,$text) local $array[1] $array[0]=$text $t=_INetSmtpMail("smtpserver", $sendername,$sender, "[helpdesk@domain.com](mailto:helpdesk@domain.com)", _ " " ,$array,"EHLO " & @ComputerName,-1 ) $error=@error if $t <> 1 Then FileWrite ( $Basepath&"check.Log", "  e-mail  "&$sendername& " "&$sender&@CRLF) FileWrite ( $Basepath&"check.Log", "  "&$error&@CRLF) Else FileWrite ( $Basepath&"check.Log", "email "&@CRLF) EndIf EndFunc ;---------------------------------------------------------------------------------------------------------- ; Com Error Handler ;---------------------------------------------------------------------------------------------------------- Func MyErrFunc() Local $HexNumber Local $strMsg $HexNumber = Hex($oMyError.Number, 8) $strMsg = " " & $HexNumber & @CRLF $strMsg &= " " & $oMyError.WinDescription & @CRLF $strMsg &= ": " & $oMyError.ScriptLine & @CRLF FileWrite ( $Basepath&"check.Log", " -: "&$strMsg) FileWrite ( $Basepath&"check.Log", "e-mail  "& @CRLF) SetError(1) Endfunc 

Modifying the code for passing parameters through the command line keys is a matter of technique.


image alt text


It was


image alt text


So it became


With the release of 1C 8.2, there was a problem - the path to chdbfl changed with the installation of the new release. Well, let's add the script:


 ;      chdbfl    $path=_FileListToArrayRec(@ProgramFilesDir&"\1cv8\","chdbfl.exe",1,1,0,2) If @error then Msgbox("","","1c  ,    " Exit Endif ;   Run ($path[1],"", @SW_HIDE,7) 

I must say, the source code Check1CD was recently published . Yes, also on AutoIT.


A similar mechanism can also be used to automatically start various regulatory mechanisms, where you need to run 1C and wait for the operation to complete.


Throwing out of the base with fiction


With various procedural operations with 1C (nightly configuration update or backup in .dt) it is important to ensure that there are no users connected to it. Of course, you can run 1C: Enterprise with the / C key . Complete the Users operation , but this is not always convenient, and then you want to write a personal letter with recommendations on how to eliminate sclerosis.


You can use the regular connectivity to 1C via the COMConnector and the AutoIT script. The code is written under 1C 8.1 and allows you to throw users out of the database with a log entry.


 global $obj = ObjCreate("v81.comconnector") $AgentConnection = $obj.ConnectAgent("tcp://servername:1540") $Cluster = $AgentConnection.GetClusters()[0] $AgentConnection.Authenticate($Cluster, "", "") $WorkingProcess = $AgentConnection.GetWorkingProcesses($Cluster)[0] $ConnectString = $WorkingProcess.HostName & ":" & $WorkingProcess.MainPort $WorkingProcessConnection = $obj.ConnectWorkingProcess($ConnectString) $WorkingProcessConnection.AddAuthentication("","") $ibDesc = $WorkingProcessConnection.CreateInfoBaseInfo() $ibDesc.Name = " " $connections = $WorkingProcessConnection.GetInfoBaseConnections($ibDesc) for $temp in $connections if $temp.username <> "" and ($temp.AppID <> "COMConsole" _ or "COM-") then FileWriteLine ( " ", @MDAY&"."&@MON&"."&@YEAR&" _ "&@HOUR&":"&@MIN&"    "&$temp.username &" _   "&$temp.AppID) $WorkingProcessConnection.Disconnect($temp) endif Next $AgentConnection="" $connect="" 

But sometimes the operation needs to be rotated at the request of the user himself, who launched the “heavy” report and hung it up 1C. If you do not want to solve these issues on your own, then simply output a shortcut to the compiled script for those who like “heavy” reports:


 $a=Msgbox(4," ?","     ?") If $a<>6 then exit Else global $obj = ObjCreate("v83.comconnector") $AgentConnection = $obj.ConnectAgent("tcp://servername:1540") $Cluster = $AgentConnection.GetClusters()[0] $AgentConnection.Authenticate($Cluster, "", "") $infobases=$AgentConnection.GetInfoBases($Cluster) for $base in $infobases if $base.name="basename" Then $connections=$AgentConnection.GetInfoBaseSessions($Cluster, $base) for $temp in $connections ;     ,     If $temp.username = @username then $AgentConnection.terminatesession($cluster,$temp) EndIf next Exit EndIf next Endif 

Another COMConnector helps to check for configuration updates, get some information from the database, and automate the establishment of users in 1C.


Technological creation of new users


In my opinion, the system administrator should create new 1C users, not 1C programmer. But the latter needs to make the process as simple as possible. To prevent the administrator from having to “look in” to each database separately, you can use another script.


For example, I’ll give you the function to create users in sample Accounting 2.0.
 ;     , ,    func _1cBuh($username, $surname,$name,$fathername) FileWriteLine($logfile, "  1 8.3  "& $Username) $obj = ObjCreate("v83.comconnector") $connect=$obj.Connect("Srvr=""servername"";Ref=""basename"";Usr=""login"";Pwd=""password"";") Local $avArray[1], $i=0 ;     $hgui = GUICreate("" , 200, 200) GUICtrlCreateLabel("   ?",2,2) GUICtrlCreateLabel("  ?",2,22) $1 = GUICtrlCreateButton("", 5, 150, 70, 25) $2= GUICtrlCreateButton("", 75, 150, 70, 25) GUISetState(@SW_SHOW) While 1 $msg = GUIGetMsg() Select Case $msg = $1 GUIDelete() $rght="" ExitLoop Case $msg = $2 GUIDelete() $rght="" ExitLoop EndSelect WEnd $mt=$connect.Metadata.Roles.find($rght) ;   $newuser= $connect..createuser() $newuser.name = $surname&" "&$name $newuser.fullname = $surname&" "&$name&" "&$fathername $newuser.StandardAuthentication = "False" $newuser.OSAuthentication="True" $newuser.OSuser="\\DOMAINNAME\"&$Username $newuser.Roles.add($mt) $newuser.write() ;    $bject=$connect.NewObject(".") $newuserS=$bject.CreateItem() $newuserS.code=$surname&" "&$name $newuserS.Description=$surname&" "&$name&" "&$fathername $newuserS.write() ;  () $query=$connect.Catalogs..() while $query.() if StringInStr ( $query.Description, "") then _ArrayAdd($avArray, $query.Description) $i+=1 endif wend $avArray[0]=$i GUICreate(" ", 200,$i*20+70) for $i=1 to $avArray[0] assign ("var"&$i, GUICtrlCreateCheckbox( $avArray[$i], 2, 0+20*$i));, 90, 20)) next $OK_Btn = GUICtrlCreateButton("", 50,$i*20+20, 60) GUISetState() While 1 $msg = GUIGetMsg() Select Case $msg = $OK_Btn For $i=1 to $avArray[0] if BitAND(GUICtrlRead(eval("var"&$i)), $GUI_CHECKED) = _ $GUI_CHECKED Then $grp=$connect... _ ($avArray[$i]).() $t=$grp..add() $t. = $newuserS. $grp.write() endif next GUIDelete() ExitLoop EndSelect WEnd $connect="" endfunc 

image alt text


Legal entities divorced too much - you need to break into columns.


It's interesting, but after changing several generations of administrators in one company from the distant past, the new ones no longer knew how to create a user manually.


Automatic database movement


If you need to transfer the 1C: Enterprise database along with its data to SQL to another server, then it is advisable to do this manually only for 1-2 databases.


With more of them, the routine will become tedious - it is better to use the script.
 #include <GUIConstantsEx.au3> local $name GUICreate("", 200, 180) GUICtrlCreateLabel("   ",2,35) $name1 = GUICtrlCreateInput($name, 2, 50, 300, 20) $OK_Btn = GUICtrlCreateButton("", 70, 180, 60) GUISetState() While 1 $msg = GUIGetMsg() Select Case $msg = $GUI_EVENT_CLOSE Exit Case $msg = $OK_Btn $name = GUICtrlRead($name1) exitloop endselect WEnd GUIDelete() GUICreate("", 200, 180) GUICtrlCreateLabel(" ",2,2) GUISetState() $constrim="DRIVER={SQL Server};SERVER=_;DATABASE=master;uid=sa;pwd=;" $adCN = ObjCreate ("ADODB.Connection") $adCN.Open ($constrim) $sQuery = "sp_detach_db @dbname = N'"&$name&"'" $adCN.Execute($sQuery) $adCN.Close GUICtrlCreateLabel(" ",2,15) $u=FileCopy ("\\_\    SQL-\"&$name&".mdf","\\_\    SQL-\"&$name&".mdf") if $u = 0 Then GUICtrlCreateLabel("!  !!!!",2,15) $OK_Btn = GUICtrlCreateButton("", 336,40, 60) GUISetState() While 1 $msg = GUIGetMsg() Select Case $msg = $GUI_EVENT_CLOSE exit Case $msg = $OK_Btn exit endselect WEnd GUIDelete() EndIf $u=FileCopy ("\\_\    _ SQL-\"&$name&".ldf","\\_\    SQL-\"&$name&".ldf") if $u = 0 Then GUICtrlCreateLabel("!  !!!!",2,15) $OK_Btn = GUICtrlCreateButton("", 336,40, 60) GUISetState() While 1 $msg = GUIGetMsg() Select Case $msg = $GUI_EVENT_CLOSE exit Case $msg = $OK_Btn exit endselect WEnd GUIDelete() EndIf GUICtrlCreateLabel("   "&$name,2,28) GUISetState() $constrim="DRIVER={SQL Server};SERVER=_;DATABASE=master;uid=sa;pwd=;" $adCN = ObjCreate ("ADODB.Connection") $adCN.Open ($constrim) $sQuery = "CREATE DATABASE "&$name&" ON (FILENAME = '    _ \"&$name&".mdf'), (FILENAME = '    \"&$name&".ldf') FOR ATTACH" $adCN.Execute($sQuery) $adCN.Close GUICtrlCreateLabel(" 1c-  "&$name,2,41) GUISetState() run ("""C:\Program Files (x86)\1cv8\common\1cestart.exe"" createinfobase_ ""Srvr=localhost;Ref="&$name&";DB="&$name&";DBMS=MSSQLServer;DBSrvr=localhost;DBUID=sa;DBPwd=""") GUICtrlCreateLabel(",  ",2,54) $OK_Btn = GUICtrlCreateButton("", 336,40, 60) GUISetState() While 1 $msg = GUIGetMsg() Select Case $msg = $GUI_EVENT_CLOSE exitloop Case $msg = $OK_Btn exitloop endselect WEnd GUIDelete() 

image alt text


The list of databases for migration can also be taken from a file, and the log can be output to a text file. Similarly, you can convert several dozen databases from file to SQL by simple upload-loading to .dt


The set ends here.


Of course, this is not all that can be automated in conjunction with 1C. But all sorts of exchanges, obtaining real-time information from 1C in other applications and other scenarios were not included in this review due to its narrow focus and specificity.


Surely you also have your own set of "know how" for administering 1C - it would be great if you share it with your colleagues in the comments.


Scripts and know-how are provided by avelor , for which many thanks to him!


')

Source: https://habr.com/ru/post/320852/


All Articles